From 6350384ee189b076372b6570331a50abbc2a053c Mon Sep 17 00:00:00 2001
From: du <13220750630.163.com>
Date: 星期四, 26 六月 2025 16:32:43 +0800
Subject: [PATCH] 流程定义

---
 ruoyi-ui/apps/web-antd/src/package/designer/plugins/translate/customTranslate.js                   |   41 
 ruoyi-ui/apps/web-antd/src/package/theme/element-variables.scss                                    |   70 
 ruoyi-ui/apps/web-antd/src/package/penal/listeners/ElementListeners.vue                            |  302 +
 ruoyi-ui/apps/web-antd/src/package/designer/plugins/content-pad/contentPadProvider.js              |  390 +
 ruoyi-ui/apps/web-antd/src/views/workflow/work/claim.vue                                           |  129 
 ruoyi-ui/apps/web-antd/src/package/designer/plugins/descriptor/flowableDescriptor.json             | 1230 +++++
 ruoyi-ui/apps/web-antd/src/package/penal/task/task-components/ScriptTask.vue                       |   85 
 ruoyi-ui/apps/web-antd/src/package/penal/task/ElementTask.vue                                      |   72 
 ruoyi-ui/apps/web-antd/src/views/workflow/work/todo.vue                                            |  132 
 ruoyi-ui/apps/web-antd/src/api/workflow/listener/index.ts                                          |   45 
 ruoyi-ui/apps/web-antd/src/package/Log.js                                                          |   99 
 ruoyi-ui/apps/web-antd/src/package/designer/plugins/palette/CustomPalette.js                       |  156 
 ruoyi-ui/apps/web-antd/src/views/workflow/work/start.vue                                           |   75 
 ruoyi-ui/apps/web-antd/src/modules/auto-place/index.js                                             |    6 
 ruoyi-ui/apps/web-antd/src/package/penal/listeners/UserTaskListeners.vue                           |  335 +
 ruoyi-ui/apps/web-antd/src/views/workflow/form/index.vue                                           |  251 +
 ruoyi-ui/apps/web-antd/src/api/workflow/category/index1.ts                                         |   63 
 ruoyi-ui/apps/web-antd/src/views/workflow/category/index.vue                                       |  335 
 ruoyi-ui/apps/web-antd/src/package/penal/multi-instance/utilSelf.js                                |   80 
 ruoyi-ui/apps/web-antd/src/package/penal/multi-instance/ElementMultiInstance.vue                   |  331 +
 ruoyi-ui/apps/web-antd/src/package/penal/listeners/template.js                                     |  178 
 ruoyi-ui/apps/web-antd/src/package/penal/properties/ElementProperties.vue                          |  141 
 ruoyi-ui/apps/web-antd/src/api/workflow/form/index.ts                                              |   24 
 ruoyi-ui/apps/web-antd/src/api/workflow/model/index.ts                                             |   47 
 ruoyi-ui/apps/web-antd/src/package/designer/plugins/palette/paletteProvider.js                     |  160 
 ruoyi-ui/apps/web-antd/src/package/penal/task/task-components/ReceiveTask.vue                      |  102 
 ruoyi-ui/apps/web-antd/src/api/workflow/work/task.ts                                               |   35 
 ruoyi-ui/apps/web-antd/src/views/workflow/work/own.vue                                             |  193 
 ruoyi-ui/apps/web-antd/src/api/workflow/identity/index.ts                                          |   11 
 ruoyi-ui/apps/web-antd/src/modules/auto-place/CustomAutoPlace.js                                   |   81 
 ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/flowable/flowableExtension.js |   72 
 ruoyi-ui/apps/web-antd/src/package/penal/signal-message/SignalAndMessage.vue                       |  110 
 ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/activiti/index.js             |   10 
 ruoyi-ui/apps/web-antd/src/package/penal/PropertiesPanel.vue                                       |  216 
 ruoyi-ui/apps/web-antd/src/package/utils.js                                                        |   71 
 ruoyi-ui/apps/web-antd/src/bootstrap.ts                                                            |    5 
 ruoyi-ui/apps/web-antd/src/package/penal/base/ElementBaseInfo.vue                                  |   80 
 ruoyi-ui/apps/web-antd/src/package/penal/listeners/utilSelf.js                                     |   81 
 ruoyi-ui/apps/web-antd/src/views/workflow/work/detail.vue                                          |  545 ++
 ruoyi-ui/apps/web-antd/src/package/penal/form/ElementForm.vue                                      |  417 +
 ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/flowable/index.js             |   10 
 ruoyi-ui/apps/web-antd/src/views/workflow/deploy/index.vue                                         |  236 
 ruoyi-ui/apps/web-antd/src/components/ProcessViewer/index.vue                                      |  237 
 ruoyi-ui/apps/web-antd/src/api/workflow/category/index.ts                                          |   76 
 ruoyi-ui/apps/web-antd/src/modules/rules/CustomRules.js                                            |   16 
 ruoyi-ui/apps/web-antd/src/package/designer/plugins/descriptor/camundaDescriptor.json              | 1087 ++++
 ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/activiti/activitiExtension.js |   73 
 ruoyi-ui/apps/web-antd/src/api/workflow/deploy/index.ts                                            |   23 
 ruoyi-ui/apps/web-antd/src/package/theme/index.scss                                                |  168 
 ruoyi-ui/apps/web-antd/src/views/workflow/model/index.vue                                          |  438 +
 ruoyi-ui/apps/web-antd/src/package/designer/plugins/palette/index.js                               |    6 
 ruoyi-ui/apps/web-antd/src/package/designer/plugins/translate/zh.js                                |  238 
 ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/camunda/extension.js          |  144 
 ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/camunda/index.js              |    7 
 ruoyi-ui/apps/web-antd/src/views/workflow/work/finished.vue                                        |  140 
 ruoyi-ui/apps/web-antd/src/modules/custom-renderer/index.js                                        |    6 
 ruoyi-ui/apps/web-antd/src/package/designer/plugins/descriptor/activitiDescriptor.json             | 1071 ++++
 ruoyi-ui/apps/web-antd/src/package/theme/process-panel.scss                                        |  129 
 ruoyi-ui/apps/web-antd/src/package/designer/plugins/content-pad/index.js                           |    6 
 ruoyi-ui/apps/web-antd/src/package/penal/index.js                                                  |    7 
 ruoyi-ui/apps/web-antd/src/package/palette/index.js                                                |    7 
 ruoyi-ui/apps/web-antd/src/views/workflow/work/index.vue                                           |  158 
 ruoyi-ui/apps/web-antd/src/modules/rules/index.js                                                  |    6 
 ruoyi-ui/apps/web-antd/src/package/designer/ProcessDesigner.vue                                    |  519 ++
 ruoyi-ui/apps/web-antd/src/package/penal/task/task-components/UserTask.vue                         |  684 ++
 ruoyi-ui/apps/web-antd/src/package/index.js                                                        |   21 
 ruoyi-ui/apps/web-antd/src/package/palette/ProcessPalette.vue                                      |  106 
 ruoyi-ui/apps/web-antd/src/package/penal/flow-condition/FlowCondition.vue                          |  142 
 ruoyi-ui/apps/web-antd/src/package/designer/index.js                                               |    7 
 ruoyi-ui/apps/web-antd/package.json                                                                |   10 
 ruoyi-ui/apps/web-antd/src/api/workflow/work/process.ts                                            |   61 
 ruoyi-ui/apps/web-antd/src/utils/min-dash.js                                                       |  694 ++
 ruoyi-ui/apps/web-antd/src/views/workflow/work/copy.vue                                            |  122 
 ruoyi-ui/apps/web-antd/src/views/workflow/listener/index.vue                                       |  469 +
 ruoyi-ui/apps/web-antd/src/package/penal/other/ElementOtherConfig.vue                              |   59 
 ruoyi-ui/apps/web-antd/src/modules/custom-renderer/CustomRenderer.js                               |   17 
 ruoyi-ui/apps/web-antd/src/package/theme/process-designer.scss                                     |  155 
 ruoyi-ui/apps/web-antd/src/package/designer/plugins/defaultEmpty.js                                |   24 
 ruoyi-ui/apps/web-antd/src/components/ProcessDesigner/index.vue                                    |  214 
 79 files changed, 14,198 insertions(+), 201 deletions(-)

diff --git a/ruoyi-ui/apps/web-antd/package.json b/ruoyi-ui/apps/web-antd/package.json
index 7336cf0..a3bb4ed 100644
--- a/ruoyi-ui/apps/web-antd/package.json
+++ b/ruoyi-ui/apps/web-antd/package.json
@@ -27,6 +27,7 @@
   },
   "dependencies": {
     "@ant-design/icons-vue": "^7.0.1",
+    "@element-plus/icons-vue": "^2.3.1",
     "@form-create/designer": "^3.2.11",
     "@form-create/element-ui": "^3.2.25",
     "@tinymce/tinymce-vue": "^6.0.1",
@@ -48,20 +49,27 @@
     "ant-design-vue": "catalog:",
     "axios": "^1.10.0",
     "bpmn-js": "^18.6.2",
+    "bpmn-js-token-simulation": "^0.38.1",
+    "codemirror": "5.65.0",
+    "codemirror-editor-vue3": "^2.8.0",
     "cropperjs": "^1.6.2",
     "crypto-js": "^4.2.0",
     "dayjs": "catalog:",
     "diagram-js": "^15.3.0",
+    "diagram-js-minimap": "^5.2.0",
     "echarts": "^5.5.1",
     "element-plus": "^2.10.2",
     "jsencrypt": "^3.3.2",
+    "lodash": "^4.17.21",
     "lodash-es": "^4.17.21",
+    "min-dash": "^4.2.3",
     "pinia": "catalog:",
     "qs": "^6.13.1",
     "tinymce": "^7.3.0",
     "unplugin-vue-components": "^0.27.3",
     "vue": "catalog:",
-    "vue-router": "catalog:"
+    "vue-router": "catalog:",
+    "x2js": "^3.4.4"
   },
   "devDependencies": {
     "@types/crypto-js": "^4.2.2",
diff --git a/ruoyi-ui/apps/web-antd/src/api/workflow/category/index.ts b/ruoyi-ui/apps/web-antd/src/api/workflow/category/index.ts
index 8126eff..4952772 100644
--- a/ruoyi-ui/apps/web-antd/src/api/workflow/category/index.ts
+++ b/ruoyi-ui/apps/web-antd/src/api/workflow/category/index.ts
@@ -1,63 +1,29 @@
-import type {
-  CategoryForm,
-  CategoryQuery,
-  CategoryTree,
-  CategoryVO,
-} from './model';
-
-import type { ID, IDS } from '#/api/common';
-
+// import request from '#/utils/request';
 import { requestClient } from '#/api/request';
-
-/**
- * 鑾峰彇娴佺▼鍒嗙被鏍戝垪琛�
- * @returns tree
- */
-export function categoryTree() {
-  return requestClient.get<CategoryTree[]>('/workflow/category/categoryTree');
+// 鏌ヨ娴佺▼鍒嗙被鍒楄〃
+export function listCategory(query) {
+  return requestClient.get('/category/list', { params: query });
 }
 
-/**
- * 鏌ヨ娴佺▼鍒嗙被鍒楄〃
- * @param params
- * @returns 娴佺▼鍒嗙被鍒楄〃
- */
-export function categoryList(params?: CategoryQuery) {
-  return requestClient.get<CategoryVO[]>(`/workflow/category/list`, { params });
+// 鏌ヨ娴佺▼鍒嗙被鍒楄〃
+export function listAllCategory(query) {
+  return requestClient.get('/category/listAll', { params: query });
+}
+// 鏌ヨ娴佺▼鍒嗙被璇︾粏
+export function getCategory(categoryId) {
+  return requestClient.get(`/category/${categoryId}`);
+}
+// 鏂板娴佺▼鍒嗙被
+export function addCategory(data) {
+  return requestClient.post('/category', { data });
 }
 
-/**
- * 鏌ヨ娴佺▼鍒嗙被璇︽儏
- * @param id id
- * @returns 娴佺▼鍒嗙被璇︽儏
- */
-export function categoryInfo(id: ID) {
-  return requestClient.get<CategoryVO>(`/workflow/category/${id}`);
+// 淇敼娴佺▼鍒嗙被
+export function updateCategory(data) {
+  return requestClient.put('/category', { data });
 }
 
-/**
- * 鏂板娴佺▼鍒嗙被
- * @param data
- * @returns void
- */
-export function categoryAdd(data: CategoryForm) {
-  return requestClient.postWithMsg<void>('/workflow/category', data);
-}
-
-/**
- * 鏇存柊娴佺▼鍒嗙被
- * @param data
- * @returns void
- */
-export function categoryUpdate(data: CategoryForm) {
-  return requestClient.putWithMsg<void>('/workflow/category', data);
-}
-
-/**
- * 鍒犻櫎娴佺▼鍒嗙被
- * @param id id
- * @returns void
- */
-export function categoryRemove(id: ID | IDS) {
-  return requestClient.deleteWithMsg<void>(`/workflow/category/${id}`);
+// 鍒犻櫎娴佺▼鍒嗙被
+export function delCategory(categoryIds) {
+  return requestClient.delete(`/category/${categoryIds}`);
 }
diff --git a/ruoyi-ui/apps/web-antd/src/api/workflow/category/index1.ts b/ruoyi-ui/apps/web-antd/src/api/workflow/category/index1.ts
new file mode 100644
index 0000000..8126eff
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/api/workflow/category/index1.ts
@@ -0,0 +1,63 @@
+import type {
+  CategoryForm,
+  CategoryQuery,
+  CategoryTree,
+  CategoryVO,
+} from './model';
+
+import type { ID, IDS } from '#/api/common';
+
+import { requestClient } from '#/api/request';
+
+/**
+ * 鑾峰彇娴佺▼鍒嗙被鏍戝垪琛�
+ * @returns tree
+ */
+export function categoryTree() {
+  return requestClient.get<CategoryTree[]>('/workflow/category/categoryTree');
+}
+
+/**
+ * 鏌ヨ娴佺▼鍒嗙被鍒楄〃
+ * @param params
+ * @returns 娴佺▼鍒嗙被鍒楄〃
+ */
+export function categoryList(params?: CategoryQuery) {
+  return requestClient.get<CategoryVO[]>(`/workflow/category/list`, { params });
+}
+
+/**
+ * 鏌ヨ娴佺▼鍒嗙被璇︽儏
+ * @param id id
+ * @returns 娴佺▼鍒嗙被璇︽儏
+ */
+export function categoryInfo(id: ID) {
+  return requestClient.get<CategoryVO>(`/workflow/category/${id}`);
+}
+
+/**
+ * 鏂板娴佺▼鍒嗙被
+ * @param data
+ * @returns void
+ */
+export function categoryAdd(data: CategoryForm) {
+  return requestClient.postWithMsg<void>('/workflow/category', data);
+}
+
+/**
+ * 鏇存柊娴佺▼鍒嗙被
+ * @param data
+ * @returns void
+ */
+export function categoryUpdate(data: CategoryForm) {
+  return requestClient.putWithMsg<void>('/workflow/category', data);
+}
+
+/**
+ * 鍒犻櫎娴佺▼鍒嗙被
+ * @param id id
+ * @returns void
+ */
+export function categoryRemove(id: ID | IDS) {
+  return requestClient.deleteWithMsg<void>(`/workflow/category/${id}`);
+}
diff --git a/ruoyi-ui/apps/web-antd/src/api/workflow/deploy/index.ts b/ruoyi-ui/apps/web-antd/src/api/workflow/deploy/index.ts
new file mode 100644
index 0000000..f1e4326
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/api/workflow/deploy/index.ts
@@ -0,0 +1,23 @@
+// import request from '#/utils/request';
+import { requestClient } from '#/api/request';
+// 鏌ヨ娴佺▼閮ㄧ讲鍒楄〃
+export function listDeploy(query) {
+  return requestClient.get('/deploy/list', { params: query });
+}
+
+export function listPublish(query) {
+  return requestClient.get('/deploy/publishList', { params: query });
+}
+// 鑾峰彇娴佺▼妯″瀷娴佺▼鍥�
+export function getBpmnXml(definitionId) {
+  return requestClient.get(`/deploy/bpmnXml/${definitionId}`);
+}
+// 淇敼娴佺▼鐘舵��
+export function changeState(params) {
+  return requestClient.put('/model',  { params: params });
+}
+
+// 鍒犻櫎娴佺▼閮ㄧ讲
+export function delDeploy(deployIds) {
+  return requestClient.delete(`/deploy/${deployIds}`);
+}
diff --git a/ruoyi-ui/apps/web-antd/src/api/workflow/form/index.ts b/ruoyi-ui/apps/web-antd/src/api/workflow/form/index.ts
new file mode 100644
index 0000000..2c473b6
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/api/workflow/form/index.ts
@@ -0,0 +1,24 @@
+// import request from '#/utils/request';
+import { requestClient } from '#/api/request';
+// 鏌ヨ娴佺▼琛ㄥ崟鍒楄〃
+export function listForm(query) {
+  return requestClient.get('/flowable/form/list', { params: query });
+}
+
+// 鏌ヨ娴佺▼琛ㄥ崟璇︾粏
+export function getForm(formId) {
+  return requestClient.get(`/form/${formId}`);
+}
+// 鏂板娴佺▼琛ㄥ崟
+export function addForm(data) {
+  return requestClient.post('/form', { data });
+}
+// 淇敼娴佺▼琛ㄥ崟
+export function updateForm(data) {
+  return requestClient.put('/form', { data });
+}
+
+// 鍒犻櫎娴佺▼琛ㄥ崟
+export function delForm(formId) {
+  return requestClient.delete(`/form/${formId}`);
+}
diff --git a/ruoyi-ui/apps/web-antd/src/api/workflow/identity/index.ts b/ruoyi-ui/apps/web-antd/src/api/workflow/identity/index.ts
new file mode 100644
index 0000000..3f22f96
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/api/workflow/identity/index.ts
@@ -0,0 +1,11 @@
+// import request from '#/utils/request';
+import { requestClient } from '#/api/request';
+// 鏌ヨ娴佺▼妯″瀷淇℃伅
+
+export function selectUser(query) {
+  return requestClient.get('/system/user/list', { params: query });
+}
+
+export function deptTreeSelect() {
+  return requestClient.get('/system/user/deptTree');
+}
diff --git a/ruoyi-ui/apps/web-antd/src/api/workflow/listener/index.ts b/ruoyi-ui/apps/web-antd/src/api/workflow/listener/index.ts
new file mode 100644
index 0000000..4480da5
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/api/workflow/listener/index.ts
@@ -0,0 +1,45 @@
+// import request from '#/utils/request';
+import { requestClient } from '#/api/request';
+// 鍒嗛〉鏌ヨ娴佺▼鐩戝惉鍣�
+export function queryListenerPage(query) {
+  return requestClient.get('/flowable/listener/queryPage', { params: query });
+}
+
+// 鍒楄〃鏌ヨ娴佺▼鐩戝惉鍣�
+export function queryListenerList(query) {
+  return requestClient.get('/flowable/listener/queryList', { params: query });
+}
+
+// 璇︾粏鏌ヨ娴佺▼鐩戝惉鍣�
+export function getListener(formId) {
+  return requestClient.get(`/flowable/listener/query/${formId}`);
+}
+
+// 鏂板娴佺▼鐩戝惉鍣�
+export function addListener(data) {
+  return requestClient.post('/flowable/listener/insert', { data });
+}
+// 淇敼娴佺▼鐩戝惉鍣�
+export function updateListener(data) {
+  return requestClient.post('/flowable/listener/update', { data });
+}
+
+// 鍒犻櫎娴佺▼鐩戝惉鍣�
+export function delListener(listenerId) {
+  return requestClient.post(`/listener/delete/${listenerId}`);
+}
+
+// 鏂板娴佺▼鐩戝惉鍣ㄥ瓧娈�
+export function insertListenerFieldAPI(data) {
+  return requestClient.post('/listener/insertField', { data });
+}
+
+// 淇敼娴佺▼鐩戝惉鍣ㄥ瓧娈�
+export function updateListenerFieldAPI(data) {
+  return requestClient.post('/listener/updateField', { data });
+}
+
+// 鍒犻櫎娴佺▼鐩戝惉鍣ㄥ瓧娈�
+export function deleteListenerFieldAPI(fieldIds) {
+  return requestClient.post(`/listener/deleteField/${fieldIds}`);
+}
diff --git a/ruoyi-ui/apps/web-antd/src/api/workflow/model/index.ts b/ruoyi-ui/apps/web-antd/src/api/workflow/model/index.ts
new file mode 100644
index 0000000..caaab58
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/api/workflow/model/index.ts
@@ -0,0 +1,47 @@
+import { requestClient } from '#/api/request';
+
+// 鏌ヨ娴佺▼妯″瀷淇℃伅
+export function listModel(query) {
+  return requestClient.get('/model/list', { params: query });
+}
+
+// 鏌ヨ鍘嗗彶娴佺▼妯″瀷淇℃伅
+export function historyModel(query) {
+  return requestClient.get('/model/historyList', { params: query });
+}
+
+export function getModel(modelId) {
+  return requestClient.get(`/model/${modelId}`);
+}
+
+// 鏂板妯″瀷淇℃伅
+export function addModel(data) {
+  return requestClient.post('/model', { data });
+}
+
+// 淇敼妯″瀷淇℃伅
+export function updateModel(data) {
+  return requestClient.put('/model', { data });
+}
+
+// 淇濆瓨娴佺▼妯″瀷
+export function saveModel(data) {
+  return requestClient.post('/model/save', { data });
+}
+
+export function latestModel(params) {
+  return requestClient.post('/model/latest', { params });
+}
+
+export function delModel(modelIds) {
+  return requestClient.delete(`/model/${modelIds}`);
+}
+
+export function deployModel(params) {
+  return requestClient.post('/model/deploy', { params });
+}
+
+// 鑾峰彇娴佺▼妯″瀷娴佺▼鍥�
+export function getBpmnXml(modelId) {
+  return requestClient.get(`/model/bpmnXml/${modelId}`);
+}
diff --git a/ruoyi-ui/apps/web-antd/src/api/workflow/work/process.ts b/ruoyi-ui/apps/web-antd/src/api/workflow/work/process.ts
new file mode 100644
index 0000000..adfdc49
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/api/workflow/work/process.ts
@@ -0,0 +1,61 @@
+import { requestClient } from '#/api/request';
+// 鏌ヨ娴佺▼鍒楄〃
+export function listProcess(query) {
+  return requestClient.get('/process/list', { params: query });
+}
+
+// 鏌ヨ娴佺▼鍒楄〃
+export function getProcessForm(query) {
+  return requestClient.get('/process/getProcessForm', { params: query });
+}
+// 閮ㄧ讲娴佺▼瀹炰緥
+export function startProcess(processDefId,data) {
+  return requestClient.post(`/process/start/${processDefId}`, { data });
+}
+// 鍒犻櫎娴佺▼瀹炰緥
+export function delProcess(ids) {
+  return requestClient.delete(`/process/instance/${ids}`);
+}
+
+// 鑾峰彇娴佺▼鍥�
+export function getBpmnXml(processDefId) {
+  return requestClient.get(`/process/bpmnXml/${processDefId}`);
+}
+
+export function detailProcess(query) {
+  return requestClient.get('/process/detail', { params: query });
+}
+
+// 鎴戠殑鍙戣捣鐨勬祦绋�
+export function listOwnProcess(query) {
+  return requestClient.get('/process/ownList', { params: query });
+}
+// 鎴戝緟鍔炵殑娴佺▼
+export function listTodoProcess(query) {
+  return requestClient.get('/process/todoList', { params: query });
+}
+// 鎴戝緟绛剧殑娴佺▼
+export function listClaimProcess(query) {
+  return requestClient.get('/process/claimList', { params: query });
+}
+// 鎴戝凡鍔炵殑娴佺▼
+export function listFinishedProcess(query) {
+  return requestClient({
+    url: '/process/finishedList',
+    method: 'get',
+    params: query
+  });
+}
+export function listFinishedProcess(query) {
+  return requestClient.get('/process/finishedList', { params: query });
+}
+
+// 鏌ヨ娴佺▼鎶勯�佸垪琛�
+export function listCopyProcess(query) {
+  return requestClient.get('/process/copyList', { params: query });
+}
+
+// 鍙栨秷鐢宠
+export function stopProcess(data) {
+  return requestClient.post('/task/stopProcess', { data });
+}
diff --git a/ruoyi-ui/apps/web-antd/src/api/workflow/work/task.ts b/ruoyi-ui/apps/web-antd/src/api/workflow/work/task.ts
new file mode 100644
index 0000000..e6e23eb
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/api/workflow/work/task.ts
@@ -0,0 +1,35 @@
+// import request from '#/utils/request';
+import { requestClient } from '#/api/request';
+// 瀹屾垚浠诲姟
+export function complete(data) {
+  return requestClient.post('/task/complete', { data });
+}
+
+// 濮旀淳浠诲姟
+export function delegate(data) {
+  return requestClient.post('/task/delegate', { data });
+}
+// 杞姙浠诲姟
+export function transfer(data) {
+  return requestClient.post('/task/transfer', { data });
+}
+// 閫�鍥炰换鍔�
+export function returnTask(data) {
+  return requestClient.post('/task/return', { data });
+}
+// 鎷掔粷浠诲姟
+export function rejectTask(data) {
+  return requestClient.post('/task/reject', { data });
+}
+// 绛炬敹浠诲姟
+export function claimTask(data) {
+  return requestClient.post('/task/claim', { data });
+}
+// 鍙��鍥炰换鍔″垪琛�
+export function returnList(data) {
+  return requestClient.post('/task/returnList', { data });
+}
+// 鎾ゅ洖浠诲姟
+export function revokeProcess(data) {
+  return requestClient.post('/task/revokeProcess', { data });
+}
diff --git a/ruoyi-ui/apps/web-antd/src/bootstrap.ts b/ruoyi-ui/apps/web-antd/src/bootstrap.ts
index adf3f37..59d0529 100644
--- a/ruoyi-ui/apps/web-antd/src/bootstrap.ts
+++ b/ruoyi-ui/apps/web-antd/src/bootstrap.ts
@@ -7,7 +7,7 @@
 import { initStores } from '@vben/stores';
 import '@vben/styles';
 import '@vben/styles/antd';
-
+import * as ElementPlusIconsVue from '@element-plus/icons-vue';
 import { useTitle } from '@vueuse/core';
 import ElementPlus from 'element-plus';
 import 'element-plus/dist/index.css'; // 鏍峰紡鏂囦欢
@@ -66,6 +66,9 @@
   app.use(formCreate);
   app.use(FcDesigner);
   app.use(Antd);
+  for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
+    app.component(key, component);
+  }
   // 鍔ㄦ�佹洿鏂版爣棰�
   watchEffect(() => {
     if (preferences.app.dynamicTitle) {
diff --git a/ruoyi-ui/apps/web-antd/src/components/ProcessDesigner/index.vue b/ruoyi-ui/apps/web-antd/src/components/ProcessDesigner/index.vue
new file mode 100644
index 0000000..7cd288b
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/components/ProcessDesigner/index.vue
@@ -0,0 +1,214 @@
+<template>
+  <div class="process-design" :style="'display: flex; height:' + height">
+    <my-process-designer
+      v-model="xmlString"
+      v-bind="controlForm"
+      keyboard
+      ref="processDesigner"
+      :events="[
+        'element.click',
+        'connection.added',
+        'connection.removed',
+        'connection.changed'
+      ]"
+      @element-click="elementClick"
+      @init-finished="initModeler"
+      @element-contextmenu="elementContextmenu"
+      @save="onSaveProcess"
+    />
+    <my-process-penal :bpmn-modeler="modeler" :prefix="controlForm.prefix" class="process-panel" />
+
+    <!-- demo config -->
+<!--    <div class="demo-control-bar">-->
+<!--      <div class="open-model-button" @click="controlDrawerVisible = true"><el-icon><setting /></el-icon></div>-->
+<!--    </div>-->
+<!--    <el-drawer v-model="controlDrawerVisible" size="400px" title="鍋忓ソ璁剧疆" append-to-body destroy-on-close>-->
+<!--      <el-form :model="controlForm" size="small" label-width="100px" class="control-form" @submit.prevent>-->
+<!--        <el-form-item label="娴佺▼ID">-->
+<!--          <el-input v-model="controlForm.processId" @change="reloadProcessDesigner(true)" />-->
+<!--        </el-form-item>-->
+<!--        <el-form-item label="娴佺▼鍚嶇О">-->
+<!--          <el-input v-model="controlForm.processName" @change="reloadProcessDesigner(true)" />-->
+<!--        </el-form-item>-->
+<!--        <el-form-item label="娴佽浆妯℃嫙">-->
+<!--          <el-switch v-model="controlForm.simulation" inactive-text="鍋滅敤" active-text="鍚敤" @change="reloadProcessDesigner()" />-->
+<!--        </el-form-item>-->
+<!--        <el-form-item label="绂佺敤鍙屽嚮">-->
+<!--          <el-switch v-model="controlForm.labelEditing" inactive-text="鍋滅敤" active-text="鍚敤" @change="changeLabelEditingStatus" />-->
+<!--        </el-form-item>-->
+<!--        <el-form-item label="鑷畾涔夋覆鏌�">-->
+<!--          <el-switch v-model="controlForm.labelVisible" inactive-text="鍋滅敤" active-text="鍚敤" @change="changeLabelVisibleStatus" />-->
+<!--        </el-form-item>-->
+<!--        <el-form-item label="娴佺▼寮曟搸">-->
+<!--          <el-radio-group v-model="controlForm.prefix" @change="reloadProcessDesigner()">-->
+<!--            <el-radio label="camunda">camunda</el-radio>-->
+<!--            <el-radio label="flowable">flowable</el-radio>-->
+<!--            <el-radio label="activiti">activiti</el-radio>-->
+<!--          </el-radio-group>-->
+<!--        </el-form-item>-->
+<!--        <el-form-item label="宸ュ叿鏍�">-->
+<!--          <el-radio-group v-model="controlForm.headerButtonSize">-->
+<!--            <el-radio label="small">small</el-radio>-->
+<!--            <el-radio label="default">default</el-radio>-->
+<!--            <el-radio label="large">large</el-radio>-->
+<!--          </el-radio-group>-->
+<!--        </el-form-item>-->
+<!--        <el-switch v-model="pageMode" active-text="dark" inactive-text="light" @change="changePageMode"></el-switch>-->
+<!--      </el-form>-->
+<!--    </el-drawer>-->
+  </div>
+</template>
+
+<script>
+import MyProcessDesigner from '#/package/designer';
+// import MyProcessPalette from '@/package/palette';
+import MyProcessPenal from '#/package/penal';
+// 鑷畾涔夋覆鏌擄紙闅愯棌浜� label 鏍囩锛�
+import CustomRenderer from '#/modules/custom-renderer';
+// 鑷畾涔夊厓绱犻�変腑鏃剁殑寮瑰嚭鑿滃崟锛堜慨鏀� 榛樿浠诲姟 涓� 鐢ㄦ埛浠诲姟锛�
+import CustomContentPadProvider from '#/package/designer/plugins/content-pad';
+// 鑷畾涔夊乏渚ц彍鍗曪紙淇敼 榛樿浠诲姟 涓� 鐢ㄦ埛浠诲姟锛�
+import CustomPaletteProvider from '#/package/designer/plugins/palette';
+
+import '#/package/theme/index.scss';
+import 'bpmn-js/dist/assets/diagram-js.css';
+import 'bpmn-js/dist/assets/bpmn-font/css/bpmn.css';
+import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css';
+
+export default {
+  name: 'ProcessDesigner',
+  props: {
+    bpmnXml: {
+      type: String,
+      required: true
+    },
+    designerForm: {
+      type: Object,
+      required: true
+    }
+  },
+  components: {
+    MyProcessDesigner,
+    MyProcessPenal
+  },
+  data () {
+    return {
+      height: document.documentElement.clientHeight - 94.5 + "px;",
+      xmlString: this.bpmnXml,
+      modeler: null,
+      controlDrawerVisible: false,
+      infoTipVisible: false,
+      pageMode: false,
+      controlForm: {
+        processId: this.designerForm.processKey || '',
+        processName: this.designerForm.processName || '',
+        simulation: true,
+        labelEditing: false,
+        labelVisible: false,
+        prefix: 'flowable',
+        headerButtonSize: 'default',
+        additionalModel: [CustomContentPadProvider, CustomPaletteProvider]
+      },
+      addis: {
+        CustomContentPadProvider,
+        CustomPaletteProvider
+      }
+    }
+  },
+  methods: {
+    reloadProcessDesigner(notDeep) {
+      this.controlForm.additionalModel = [];
+      for (const key in this.addis) {
+        if (this.addis[key]) {
+          this.controlForm.additionalModel.push(this.addis[key]);
+        }
+      }
+      !notDeep && (this.xmlString = undefined);
+      this.reloadIndex += 1;
+      this.modeler = null; // 閬垮厤 panel 寮傚父
+    },
+    changeLabelEditingStatus(status) {
+      this.addis.labelEditing = status ? { labelEditingProvider: ['value', ''] } : false;
+      this.reloadProcessDesigner();
+    },
+    changeLabelVisibleStatus(status) {
+      this.addis.customRenderer = status ? CustomRenderer : false;
+      this.reloadProcessDesigner();
+    },
+    elementClick(element) {
+      this.element = element;
+    },
+    initModeler(modeler) {
+      setTimeout(() => {
+        this.modeler = modeler;
+      }, 10);
+    },
+    elementContextmenu(element) {
+    },
+    onSaveProcess(saveData) {
+      this.$emit('save', saveData);
+    }
+  }
+}
+</script>
+
+<style lang="scss">
+body {
+  overflow: hidden;
+  margin: 0;
+  box-sizing: border-box;
+}
+body,
+body * {
+  /* 婊氬姩鏉� */
+  &::-webkit-scrollbar-track-piece {
+    background-color: #fff; /*婊氬姩鏉$殑鑳屾櫙棰滆壊*/
+    -webkit-border-radius: 0; /*婊氬姩鏉$殑鍦嗚瀹藉害*/
+  }
+
+  &::-webkit-scrollbar {
+    width: 10px; /*婊氬姩鏉$殑瀹藉害*/
+    height: 8px; /*婊氬姩鏉$殑楂樺害*/
+  }
+
+  &::-webkit-scrollbar-thumb:vertical {
+    /*鍨傜洿婊氬姩鏉$殑鏍峰紡*/
+    height: 50px;
+    background-color: rgba(153, 153, 153, 0.5);
+    -webkit-border-radius: 4px;
+    outline: 2px solid #fff;
+    outline-offset: -2px;
+    border: 2px solid #fff;
+  }
+
+  &::-webkit-scrollbar-thumb {
+    /*婊氬姩鏉$殑hover鏍峰紡*/
+    background-color: rgba(159, 159, 159, 0.3);
+    -webkit-border-radius: 4px;
+  }
+
+  &::-webkit-scrollbar-thumb:hover {
+    /*婊氬姩鏉$殑hover鏍峰紡*/
+    background-color: rgba(159, 159, 159, 0.5);
+    -webkit-border-radius: 4px;
+  }
+}
+.demo-control-bar {
+  position: fixed;
+  right: 8px;
+  bottom: 48px;
+  z-index: 1;
+}
+.open-model-button {
+  width: 48px;
+  height: 48px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  border-radius: 4px;
+  font-size: 32px;
+  background: rgba(64, 158, 255, 1);
+  color: #ffffff;
+  cursor: pointer;
+}
+</style>
diff --git a/ruoyi-ui/apps/web-antd/src/components/ProcessViewer/index.vue b/ruoyi-ui/apps/web-antd/src/components/ProcessViewer/index.vue
new file mode 100644
index 0000000..24ce40f
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/components/ProcessViewer/index.vue
@@ -0,0 +1,237 @@
+<template>
+  <div class="process-viewer">
+    <div class="process-canvas" style="height: 100%;" ref="processCanvas" v-show="!isLoading" />
+    <!-- 鑷畾涔夌澶存牱寮忥紝鐢ㄤ簬鎴愬姛鐘舵�佷笅娴佺▼杩炵嚎绠ご -->
+    <defs ref="customSuccessDefs">
+      <marker id="sequenceflow-end-white-success" viewBox="0 0 20 20" refX="11" refY="10" markerWidth="10" markerHeight="10" orient="auto">
+        <path class="success-arrow" d="M 1 5 L 11 10 L 1 15 Z" style="stroke-width: 1px; stroke-linecap: round; stroke-dasharray: 10000, 1;"></path>
+      </marker>
+      <marker id="conditional-flow-marker-white-success" viewBox="0 0 20 20" refX="-1" refY="10" markerWidth="10" markerHeight="10" orient="auto">
+        <path class="success-conditional" d="M 0 10 L 8 6 L 16 10 L 8 14 Z" style="stroke-width: 1px; stroke-linecap: round; stroke-dasharray: 10000, 1;"></path>
+      </marker>
+    </defs>
+    <!-- 鑷畾涔夌澶存牱寮忥紝鐢ㄤ簬澶辫触鐘舵�佷笅娴佺▼杩炵嚎绠ご -->
+    <defs ref="customFailDefs">
+      <marker id="sequenceflow-end-white-fail" viewBox="0 0 20 20" refX="11" refY="10" markerWidth="10" markerHeight="10" orient="auto">
+        <path class="fail-arrow" d="M 1 5 L 11 10 L 1 15 Z" style="stroke-width: 1px; stroke-linecap: round; stroke-dasharray: 10000, 1;"></path>
+      </marker>
+      <marker id="conditional-flow-marker-white-fail" viewBox="0 0 20 20" refX="-1" refY="10" markerWidth="10" markerHeight="10" orient="auto">
+        <path class="fail-conditional" d="M 0 10 L 8 6 L 16 10 L 8 14 Z" style="stroke-width: 1px; stroke-linecap: round; stroke-dasharray: 10000, 1;"></path>
+      </marker>
+    </defs>
+    <!-- 宸插畬鎴愯妭鐐规偓娴脊绐� -->
+    <el-dialog class="comment-dialog" :title="dlgTitle || '瀹℃壒璁板綍'" v-model="dialogVisible">
+      <el-row>
+        <el-table :data="taskCommentList" border header-cell-class-name="table-header-gray">
+          <el-table-column label="搴忓彿" header-align="center" align="center" type="index" width="55px" />
+          <el-table-column label="鍊欓�夊姙鐞�" prop="candidate" width="150px" align="center" />
+          <el-table-column label="瀹為檯鍔炵悊" prop="assigneeName" width="100px" align="center" />
+          <el-table-column label="澶勭悊鏃堕棿" prop="createTime" width="140px" align="center" />
+          <el-table-column label="鍔炵粨鏃堕棿" prop="finishTime" width="140px" align="center" />
+          <el-table-column label="鑰楁椂" prop="duration" width="100px" align="center" />
+          <el-table-column label="瀹℃壒鎰忚" align="center">
+            <template #default="scope">
+              {{scope.row.commentList&&scope.row.commentList[0]?scope.row.commentList[0].fullMessage:''}}
+            </template>
+          </el-table-column>
+        </el-table>
+      </el-row>
+    </el-dialog>
+    <div style="position: absolute; top: 0; left: 0; width: 100%;">
+      <el-row type="flex" justify="end">
+        <el-button-group key="scale-control">
+          <el-button :plain="true" :disabled="defaultZoom <= 0.3" icon="ZoomOut" @click="processZoomOut()" />
+          <el-button style="width: 90px;">{{ Math.floor(this.defaultZoom * 10 * 10) + "%" }}</el-button>
+          <el-button :plain="true" :disabled="defaultZoom >= 3.9" icon="ZoomIn" @click="processZoomIn()" />
+          <el-button icon="ScaleToOriginal" @click="processReZoom()" />
+          <slot />
+        </el-button-group>
+      </el-row>
+    </div>
+  </div>
+</template>
+
+<script>
+import '#/package/theme/index.scss';
+import BpmnViewer from 'bpmn-js/lib/Viewer';
+import MoveCanvasModule from 'diagram-js/lib/navigation/movecanvas';
+
+export default {
+  props: {
+    xml: {
+      type: String
+    },
+    finishedInfo: {
+      type: Object
+    },
+    // 鎵�鏈夎妭鐐瑰鎵硅褰�
+    allCommentList: {
+      type: Array
+    }
+  },
+  data () {
+    return {
+      dialogVisible: false,
+      dlgTitle: undefined,
+      defaultZoom: 1,
+      // 鏄惁姝e湪鍔犺浇娴佺▼鍥�
+      isLoading: false,
+      bpmnViewer: undefined,
+      // 宸插畬鎴愭祦绋嬪厓绱�
+      processNodeInfo: undefined,
+      // 褰撳墠浠诲姟id
+      selectTaskId: undefined,
+      // 浠诲姟鑺傜偣瀹℃壒璁板綍
+      taskCommentList: [],
+      // 宸插畬鎴愪换鍔℃偓娴欢杩烼imer
+      hoverTimer: null
+    }
+  },
+  watch: {
+    xml: {
+      handler(newXml) {
+        this.importXML(newXml);
+      },
+      immediate: true
+    },
+    finishedInfo: {
+      handler(newInfo) {
+        this.setProcessStatus(newInfo);
+      },
+      immediate: true
+    }
+  },
+  created() {
+    this.$nextTick(() => {
+      this.importXML(this.xml)
+      this.setProcessStatus(this.finishedInfo);
+    })
+  },
+  methods: {
+    processReZoom() {
+      this.defaultZoom = 1;
+      this.bpmnViewer.get('canvas').zoom('fit-viewport', 'auto');
+    },
+    processZoomIn(zoomStep = 0.1) {
+      let newZoom = Math.floor(this.defaultZoom * 100 + zoomStep * 100) / 100;
+      if (newZoom > 4) {
+        throw new Error('[Process Designer Warn ]: The zoom ratio cannot be greater than 4');
+      }
+      this.defaultZoom = newZoom;
+      this.bpmnViewer.get('canvas').zoom(this.defaultZoom);
+    },
+    processZoomOut(zoomStep = 0.1) {
+      let newZoom = Math.floor(this.defaultZoom * 100 - zoomStep * 100) / 100;
+      if (newZoom < 0.2) {
+        throw new Error('[Process Designer Warn ]: The zoom ratio cannot be less than 0.2');
+      }
+      this.defaultZoom = newZoom;
+      this.bpmnViewer.get('canvas').zoom(this.defaultZoom);
+    },
+    // 娴佺▼鍥鹃瑙堟竻绌�
+    clearViewer() {
+      if (this.$refs.processCanvas) {
+        this.$refs.processCanvas.innerHTML = '';
+      }
+      if (this.bpmnViewer) {
+        this.bpmnViewer.destroy();
+      }
+      this.bpmnViewer = null;
+    },
+    // 娣诲姞鑷畾涔夌澶�
+    addCustomDefs() {
+      const canvas = this.bpmnViewer.get('canvas');
+      const svg = canvas._svg;
+      const customSuccessDefs = this.$refs.customSuccessDefs;
+      const customFailDefs = this.$refs.customFailDefs;
+      svg.appendChild(customSuccessDefs);
+      svg.appendChild(customFailDefs);
+    },
+    // 浠诲姟鎮诞寮圭獥
+    onSelectElement(element) {
+      this.selectTaskId = undefined;
+      this.dlgTitle = undefined;
+
+      if (this.processNodeInfo == null || this.processNodeInfo.finishedTaskSet == null) return;
+
+      if (element == null || this.processNodeInfo.finishedTaskSet.indexOf(element.id) === -1) {
+        return;
+      }
+
+      this.selectTaskId = element.id;
+      this.dlgTitle = element.businessObject ? element.businessObject.name : undefined;
+      // 璁$畻褰撳墠鎮诞浠诲姟瀹℃壒璁板綍锛屽鏋滆褰曚负绌轰笉鏄剧ず寮圭獥
+      this.taskCommentList = (this.allCommentList || []).filter(item => {
+        return item.activityId === this.selectTaskId;
+      });
+      this.dialogVisible = true;
+    },
+    // 鏄剧ず娴佺▼鍥�
+    async importXML(xml) {
+      this.clearViewer();
+      if (xml != null && xml !== '') {
+        try {
+          this.bpmnViewer = new BpmnViewer({
+            additionalModules: [
+              // 绉诲姩鏁翠釜鐢诲竷
+              MoveCanvasModule
+            ],
+            container: this.$refs.processCanvas,
+          });
+          // 浠诲姟鑺傜偣鎮诞浜嬩欢
+          this.bpmnViewer.on('element.click', ({ element }) => {
+            this.onSelectElement(element);
+          });
+
+          this.isLoading = true;
+          await this.bpmnViewer.importXML(xml);
+          this.addCustomDefs();
+        } catch (e) {
+          this.clearViewer();
+        } finally {
+          this.isLoading = false;
+          this.setProcessStatus(this.processNodeInfo);
+        }
+      }
+    },
+    // 璁剧疆娴佺▼鍥惧厓绱犵姸鎬�
+    setProcessStatus (processNodeInfo) {
+      this.processNodeInfo = processNodeInfo;
+      if (this.isLoading || this.processNodeInfo == null || this.bpmnViewer == null) return;
+      let { finishedTaskSet, rejectedTaskSet, unfinishedTaskSet, finishedSequenceFlowSet } = this.processNodeInfo;
+      const canvas = this.bpmnViewer.get('canvas');
+      const elementRegistry = this.bpmnViewer.get('elementRegistry');
+      if (Array.isArray(finishedSequenceFlowSet)) {
+        finishedSequenceFlowSet.forEach(item => {
+          if (item != null) {
+            canvas.addMarker(item, 'success');
+            let element = elementRegistry.get(item);
+            const conditionExpression = element.businessObject.conditionExpression;
+            if (conditionExpression) {
+              canvas.addMarker(item, 'condition-expression');
+            }
+          }
+        });
+      }
+      if (Array.isArray(finishedTaskSet)) {
+        finishedTaskSet.forEach(item => canvas.addMarker(item, 'success'));
+      }
+      if (Array.isArray(unfinishedTaskSet)) {
+        unfinishedTaskSet.forEach(item => canvas.addMarker(item, 'primary'));
+      }
+      if (Array.isArray(rejectedTaskSet)) {
+        rejectedTaskSet.forEach(item => {
+          if (item != null) {
+            let element = elementRegistry.get(item);
+            if (element.type.includes('Task')) {
+              canvas.addMarker(item, 'danger');
+            } else {
+              canvas.addMarker(item, 'warning');
+            }
+          }
+        })
+      }
+    }
+  }
+}
+</script>
diff --git a/ruoyi-ui/apps/web-antd/src/modules/auto-place/CustomAutoPlace.js b/ruoyi-ui/apps/web-antd/src/modules/auto-place/CustomAutoPlace.js
new file mode 100644
index 0000000..64a1560
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/modules/auto-place/CustomAutoPlace.js
@@ -0,0 +1,81 @@
+import AutoPlace from 'diagram-js/lib/features/auto-place/AutoPlace';
+
+export default function CustomAutoPlace(eventBus, modeling) {
+  AutoPlace.call(this, eventBus, modeling, 3000);
+
+  eventBus.on('autoPlace', 3000, function(context) {
+    const shape = context.shape;
+    const source = context.source;
+
+    return getNewCustomShapePosition(source, shape);
+  });
+
+  this.append = function(source, shape, hints) {
+    eventBus.fire('autoPlace.start', {
+      source: source,
+      shape: shape
+    });
+
+    // allow others to provide the position
+    var position = eventBus.fire('autoPlace', {
+      source: source,
+      shape: shape
+    });
+
+    console.log('hints', hints, 'position', position);
+
+    var newShape = modeling.appendShape(source, shape, position, source.parent, hints);
+
+    eventBus.fire('autoPlace.end', {
+      source: source,
+      shape: newShape
+    });
+
+    return newShape;
+  };
+}
+
+export function asTRBL(bounds) {
+  return {
+    top: bounds.y,
+    right: bounds.x + (bounds.width || 0),
+    bottom: bounds.y + (bounds.height || 0),
+    left: bounds.x
+  };
+}
+
+export function roundPoint(point) {
+  return {
+    x: Math.round(point.x),
+    y: Math.round(point.y)
+  };
+}
+
+export function getMid(bounds) {
+  return roundPoint({
+    x: bounds.x + (bounds.width || 0) / 2,
+    y: bounds.y + (bounds.height || 0) / 2
+  });
+}
+
+export function getNewCustomShapePosition(source, element, hints) {
+  if (!hints) {
+    hints = {};
+  }
+
+  var distance = hints.defaultDistance || 50;
+
+  var sourceMid = getMid(source);
+  var sourceTrbl = asTRBL(source);
+
+  // simply put element right next to source
+  return {
+    x: sourceMid.x,
+    y: sourceTrbl.bottom + distance + element.height / 2
+  };
+}
+
+const F = function() {}; // 鏍稿績锛屽埄鐢ㄧ┖瀵硅薄浣滀负涓粙锛�
+F.prototype = AutoPlace.prototype; // 鏍稿績锛屽皢鐖剁被鐨勫師鍨嬭祴鍊肩粰绌哄璞锛�
+CustomAutoPlace.prototype = new F(); // 鏍稿績锛屽皢 F鐨勫疄渚嬭祴鍊肩粰瀛愮被锛�
+CustomAutoPlace.prototype.constructor = AutoPlace; // 淇瀛愮被CustomRenderer鐨勬瀯閫犲櫒鎸囧悜锛岄槻姝㈠師鍨嬮摼鐨勬贩涔憋紱
diff --git a/ruoyi-ui/apps/web-antd/src/modules/auto-place/index.js b/ruoyi-ui/apps/web-antd/src/modules/auto-place/index.js
new file mode 100644
index 0000000..44dc28e
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/modules/auto-place/index.js
@@ -0,0 +1,6 @@
+import CustomAutoPlace from './CustomAutoPlace';
+
+export default {
+  __init__: ['autoPlace'],
+  autoPlace: ['type', CustomAutoPlace]
+};
diff --git a/ruoyi-ui/apps/web-antd/src/modules/custom-renderer/CustomRenderer.js b/ruoyi-ui/apps/web-antd/src/modules/custom-renderer/CustomRenderer.js
new file mode 100644
index 0000000..6e9988b
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/modules/custom-renderer/CustomRenderer.js
@@ -0,0 +1,17 @@
+import BpmnRenderer from 'bpmn-js/lib/draw/BpmnRenderer';
+
+export default function CustomRenderer(eventBus, styles, pathMap, canvas, textRenderer) {
+  const config = {
+    defaultFillColor: '',
+    defaultStrokeColor: '#8b238f',
+    defaultLabelColor: '#2dd257'
+  };
+  BpmnRenderer.call(this, config, eventBus, styles, pathMap, canvas, textRenderer, 2000);
+}
+
+CustomRenderer.$inject = ['eventBus', 'styles', 'pathMap', 'canvas', 'textRenderer'];
+
+const F = function() {}; // 鏍稿績锛屽埄鐢ㄧ┖瀵硅薄浣滀负涓粙锛�
+F.prototype = BpmnRenderer.prototype; // 鏍稿績锛屽皢鐖剁被鐨勫師鍨嬭祴鍊肩粰绌哄璞锛�
+CustomRenderer.prototype = new F(); // 鏍稿績锛屽皢 F鐨勫疄渚嬭祴鍊肩粰瀛愮被锛�
+CustomRenderer.prototype.constructor = CustomRenderer; // 淇瀛愮被CustomRenderer鐨勬瀯閫犲櫒鎸囧悜锛岄槻姝㈠師鍨嬮摼鐨勬贩涔憋紱
diff --git a/ruoyi-ui/apps/web-antd/src/modules/custom-renderer/index.js b/ruoyi-ui/apps/web-antd/src/modules/custom-renderer/index.js
new file mode 100644
index 0000000..ab0b4ef
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/modules/custom-renderer/index.js
@@ -0,0 +1,6 @@
+import CustomRenderer from './CustomRenderer';
+
+export default {
+  __init__: ['customRenderer'],
+  customRenderer: ['type', CustomRenderer]
+};
diff --git a/ruoyi-ui/apps/web-antd/src/modules/rules/CustomRules.js b/ruoyi-ui/apps/web-antd/src/modules/rules/CustomRules.js
new file mode 100644
index 0000000..4f659cc
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/modules/rules/CustomRules.js
@@ -0,0 +1,16 @@
+import BpmnRules from 'bpmn-js/lib/features/rules/BpmnRules';
+import inherits from 'inherits';
+
+export default function CustomRules(eventBus) {
+  BpmnRules.call(this, eventBus);
+}
+
+inherits(CustomRules, BpmnRules);
+
+CustomRules.prototype.canDrop = function() {
+  return false;
+};
+
+CustomRules.prototype.canMove = function() {
+  return false;
+};
diff --git a/ruoyi-ui/apps/web-antd/src/modules/rules/index.js b/ruoyi-ui/apps/web-antd/src/modules/rules/index.js
new file mode 100644
index 0000000..bc0a21f
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/modules/rules/index.js
@@ -0,0 +1,6 @@
+import CustomRules from './CustomRules';
+
+export default {
+  __init__: ['customRules'],
+  customRules: ['type', CustomRules]
+};
diff --git a/ruoyi-ui/apps/web-antd/src/package/Log.js b/ruoyi-ui/apps/web-antd/src/package/Log.js
new file mode 100644
index 0000000..f1605fd
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/Log.js
@@ -0,0 +1,99 @@
+function Log() {}
+
+Log.prototype.type = ['primary', 'success', 'warn', 'error', 'info'];
+
+Log.prototype.typeColor = function(type) {
+  let color = '';
+  switch (type) {
+    case 'primary':
+      color = '#2d8cf0';
+      break;
+    case 'success':
+      color = '#19be6b';
+      break;
+    case 'info':
+      color = '#909399';
+      break;
+    case 'warn':
+      color = '#ff9900';
+      break;
+    case 'error':
+      color = '#f03f14';
+      break;
+    default:
+      color = '#35495E';
+      break;
+  }
+  return color;
+};
+
+Log.prototype.isArray = function(obj) {
+  return Object.prototype.toString.call(obj) === '[object Array]';
+};
+
+Log.prototype.print = function(text, type = 'default', back = false) {
+  if (typeof text === 'object') {
+    // 濡傛灉鏄皪璞″墖瑾跨敤鎵撳嵃灏嶈薄鏂瑰紡
+    this.isArray(text) ? console.table(text) : console.dir(text);
+    return;
+  }
+  if (back) {
+    // 濡傛灉鏄墦鍗板付鑳屾櫙鍦栫殑
+    console.log(`%c ${text} `, `background:${this.typeColor(type)}; padding: 2px; border-radius: 4px; color: #fff;`);
+  } else {
+    console.log(
+      `%c ${text} `,
+      `border: 1px solid ${this.typeColor(type)};
+        padding: 2px; border-radius: 4px;
+        color: ${this.typeColor(type)};`
+    );
+  }
+};
+
+Log.prototype.printBack = function(type = 'primary', title) {
+  this.print(type, title, true);
+};
+
+Log.prototype.pretty = function(type = 'primary', title, text) {
+  if (typeof text === 'object') {
+    console.group('Console Group', title);
+    console.log(
+      `%c ${title}`,
+      `background:${this.typeColor(type)};border:1px solid ${this.typeColor(type)};
+        padding: 1px; border-radius: 4px; color: #fff;`
+    );
+    this.isArray(text) ? console.table(text) : console.dir(text);
+    console.groupEnd();
+    return;
+  }
+  console.log(
+    `%c ${title} %c ${text} %c`,
+    `background:${this.typeColor(type)};border:1px solid ${this.typeColor(type)};
+      padding: 1px; border-radius: 4px 0 0 4px; color: #fff;`,
+    `border:1px solid ${this.typeColor(type)};
+      padding: 1px; border-radius: 0 4px 4px 0; color: ${this.typeColor(type)};`,
+    'background:transparent'
+  );
+};
+
+Log.prototype.prettyPrimary = function(title, ...text) {
+  text.forEach(t => this.pretty('primary', title, t));
+};
+
+Log.prototype.prettySuccess = function(title, ...text) {
+  text.forEach(t => this.pretty('success', title, t));
+};
+
+Log.prototype.prettyWarn = function(title, ...text) {
+  text.forEach(t => this.pretty('warn', title, t));
+};
+
+Log.prototype.prettyError = function(title, ...text) {
+  text.forEach(t => this.pretty('error', title, t));
+};
+
+Log.prototype.prettyInfo = function(title, ...text) {
+  text.forEach(t => this.pretty('info', title, t));
+};
+
+export default new Log();
diff --git a/ruoyi-ui/apps/web-antd/src/package/designer/ProcessDesigner.vue b/ruoyi-ui/apps/web-antd/src/package/designer/ProcessDesigner.vue
new file mode 100644
index 0000000..a343aa0
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/designer/ProcessDesigner.vue
@@ -0,0 +1,519 @@
+<template>
+  <div class="my-process-designer">
+    <div class="my-process-designer__header">
+      <slot name="control-header"></slot>
+      <template v-if="!$slots['control-header']">
+        <el-button-group key="file-control">
+          <el-button :size="headerButtonSize" :type="headerButtonType" icon="el-icon-edit-outline" @click="onSave">淇濆瓨娴佺▼</el-button>
+          <el-button :size="headerButtonSize" :type="headerButtonType" :icon="FolderOpened" @click="$refs.refFile.click()">鎵撳紑鏂囦欢</el-button>
+          <el-tooltip effect="light">
+            <template #content>
+              <a :size="headerButtonSize"  @click="downloadProcessAsXml()" class="link-type">涓嬭浇涓篨ML鏂囦欢</a>
+              <br />
+              <a :size="headerButtonSize" type="text" @click="downloadProcessAsSvg()" class="link-type">涓嬭浇涓篠VG鏂囦欢</a>
+              <br />
+              <a :size="headerButtonSize" type="text" @click="downloadProcessAsBpmn()" class="link-type">涓嬭浇涓築PMN鏂囦欢</a>
+            </template>
+            <el-button :size="headerButtonSize" :type="headerButtonType" :icon="Download">涓嬭浇鏂囦欢</el-button>
+          </el-tooltip>
+          <el-tooltip effect="light">
+            <template #content>
+              <a :size="headerButtonSize" type="text" @click="previewProcessXML" class="link-type">棰勮XML</a>
+              <br />
+               <a :size="headerButtonSize" type="text" @click="previewProcessJson" class="link-type">棰勮JSON</a>
+            </template>
+            <el-button :size="headerButtonSize" :type="headerButtonType" :icon="View">棰勮</el-button>
+          </el-tooltip>
+          <el-tooltip v-if="simulation" effect="light" :content="this.simulationStatus ? '閫�鍑烘ā鎷�' : '寮�鍚ā鎷�'">
+            <el-button :size="headerButtonSize" :type="headerButtonType" :icon="Cpu" @click="processSimulation">
+              妯℃嫙
+            </el-button>
+          </el-tooltip>
+        </el-button-group>
+        <el-button-group key="align-control">
+          <el-tooltip effect="light" content="鍚戝乏瀵归綈">
+            <el-button :size="headerButtonSize" class="align align-left" :icon="Histogram" @click="elementsAlign('left')" />
+          </el-tooltip>
+          <el-tooltip effect="light" content="鍚戝彸瀵归綈">
+            <el-button :size="headerButtonSize" class="align align-right" :icon="Histogram" @click="elementsAlign('right')" />
+          </el-tooltip>
+          <el-tooltip effect="light" content="鍚戜笂瀵归綈">
+            <el-button :size="headerButtonSize" class="align align-top" :icon="Histogram" @click="elementsAlign('top')" />
+          </el-tooltip>
+          <el-tooltip effect="light" content="鍚戜笅瀵归綈">
+            <el-button :size="headerButtonSize" class="align align-bottom" :icon="Histogram" @click="elementsAlign('bottom')" />
+          </el-tooltip>
+          <el-tooltip effect="light" content="姘村钩灞呬腑">
+            <el-button :size="headerButtonSize" class="align align-center" :icon="Histogram" @click="elementsAlign('center')" />
+          </el-tooltip>
+          <el-tooltip effect="light" content="鍨傜洿灞呬腑">
+            <el-button :size="headerButtonSize" class="align align-middle" :icon="Histogram" @click="elementsAlign('middle')" />
+          </el-tooltip>
+        </el-button-group>
+        <el-button-group key="scale-control">
+          <el-tooltip effect="light" content="缂╁皬瑙嗗浘">
+            <el-button :size="headerButtonSize" :disabled="defaultZoom < 0.2" :icon="ZoomOut" @click="processZoomOut()" />
+          </el-tooltip>
+          <el-button :size="headerButtonSize">{{ Math.floor(this.defaultZoom * 10 * 10) + "%" }}</el-button>
+          <el-tooltip effect="light" content="鏀惧ぇ瑙嗗浘">
+            <el-button :size="headerButtonSize" :disabled="defaultZoom > 4" :icon="ZoomIn" @click="processZoomIn()" />
+          </el-tooltip>
+          <el-tooltip effect="light" content="閲嶇疆瑙嗗浘骞跺眳涓�">
+            <el-button :size="headerButtonSize" :icon="ScaleToOriginal" @click="processReZoom()" />
+          </el-tooltip>
+        </el-button-group>
+        <el-button-group key="stack-control">
+          <el-tooltip effect="light" content="鎾ら攢">
+            <el-button :size="headerButtonSize" :disabled="!revocable" :icon="RefreshLeft" @click="processUndo()" />
+          </el-tooltip>
+          <el-tooltip effect="light" content="鎭㈠">
+            <el-button :size="headerButtonSize" :disabled="!recoverable" :icon="RefreshRight" @click="processRedo()" />
+          </el-tooltip>
+          <el-tooltip effect="light" content="閲嶆柊缁樺埗">
+            <el-button :size="headerButtonSize" :icon="Refresh" @click="processRestart" />
+          </el-tooltip>
+        </el-button-group>
+      </template>
+      <!-- 鐢ㄤ簬鎵撳紑鏈湴鏂囦欢-->
+      <input type="file" id="files" ref="refFile" style="display: none" accept=".xml, .bpmn" @change="importLocalFile" />
+    </div>
+    <div class="my-process-designer__container">
+      <div class="my-process-designer__canvas" ref="bpmn-canvas"></div>
+    </div>
+    <el-dialog :title="`棰勮${previewType}`" width="60%" v-model="previewModelVisible" append-to-body destroy-on-close>
+        <Codemirror
+          v-model:value="previewResult"
+          :options="cmOptions"
+          border
+          :height="600"
+        />
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import BpmnModeler from "bpmn-js/lib/Modeler";
+import DefaultEmptyXML from "./plugins/defaultEmpty";
+// 缈昏瘧鏂规硶
+import customTranslate from "./plugins/translate/customTranslate";
+import translationsCN from "./plugins/translate/zh";
+// 妯℃嫙娴佽浆娴佺▼
+import tokenSimulation from "bpmn-js-token-simulation";
+// 鏍囩瑙f瀽鏋勫缓鍣�
+// import bpmnPropertiesProvider from "bpmn-js-properties-panel/lib/provider/bpmn";
+// 鏍囩瑙f瀽 Moddle
+import camundaModdleDescriptor from './plugins/descriptor/camundaDescriptor.json';
+import activitiModdleDescriptor from './plugins/descriptor/activitiDescriptor.json';
+import flowableModdleDescriptor from './plugins/descriptor/flowableDescriptor.json';
+// 鏍囩瑙f瀽 Extension
+import camundaModdleExtension from './plugins/extension-moddle/camunda';
+import activitiModdleExtension from './plugins/extension-moddle/activiti';
+import flowableModdleExtension from './plugins/extension-moddle/flowable';
+// 寮曞叆json杞崲涓庨珮浜�
+// import convert from "xml-js";
+import X2JS from "x2js";
+
+import Codemirror from 'codemirror-editor-vue3';
+import 'codemirror/theme/monokai.css'
+import 'codemirror/mode/javascript/javascript.js';
+import 'codemirror/mode/xml/xml.js';
+
+export default {
+  name: "MyProcessDesigner",
+  componentName: "MyProcessDesigner",
+  components: {
+    Codemirror
+  },
+  setup() {
+    return {
+      Histogram, Cpu, Refresh, RefreshLeft, RefreshRight, ZoomOut, ZoomIn, View, Download, FolderOpened, ScaleToOriginal
+    }
+  },
+  emits: ['destroy', 'init-finished', 'commandStack-changed', 'update:modelValue', 'change', 'canvas-viewbox-changed', 'element-click',"save","connection-added"],
+  props: {
+    modelValue: String, // xml 瀛楃涓�
+    processId: String,
+    processName: String,
+    translations: Object, // 鑷畾涔夌殑缈昏瘧鏂囦欢
+    options: {
+      type: Object,
+      default: () => ({})
+    }, // 鑷畾涔夌殑缈昏瘧鏂囦欢
+    additionalModel: [Object, Array], // 鑷畾涔塵odel
+    moddleExtension: Object, // 鑷畾涔塵oddle
+    onlyCustomizeAddi: {
+      type: Boolean,
+      default: false
+    },
+    onlyCustomizeModdle: {
+      type: Boolean,
+      default: false
+    },
+    simulation: {
+      type: Boolean,
+      default: true
+    },
+    keyboard: {
+      type: Boolean,
+      default: true
+    },
+    prefix: {
+      type: String,
+      default: "camunda"
+    },
+    events: {
+      type: Array,
+      default: () => ["element.click"]
+    },
+    headerButtonSize: {
+      type: String,
+      default: "small",
+      validator: value => ["default", "medium", "small", "mini"].indexOf(value) !== -1
+    },
+    headerButtonType: {
+      type: String,
+      default: "primary",
+      validator: value => ["default", "primary", "success", "warning", "danger", "info"].indexOf(value) !== -1
+    }
+  },
+  data() {
+    return {
+      defaultZoom: 1,
+      previewModelVisible: false,
+      simulationStatus: false,
+      previewResult: "",
+      previewType: "xml",
+      recoverable: false,
+      revocable: false,
+      cmOptions: {
+        mode: 'xml', // 璇█妯″紡
+        theme: 'monokai', // 涓婚
+        lineNumbers: true, // 鏄剧ず琛屽彿
+        smartIndent: true, // 鏅鸿兘缂╄繘
+        readOnly: true,
+        indentUnit: 2, // 鏅鸿兘缂╄繘鍗曚綅涓�4涓┖鏍奸暱搴�
+        foldGutter: true, // 鍚敤琛屾Ы涓殑浠g爜鎶樺彔
+        styleActiveLine: true // 鏄剧ず閫変腑琛岀殑鏍峰紡
+      }
+    };
+  },
+  computed: {
+    additionalModules() {
+      const Modules = [];
+      // 浠呬繚鐣欑敤鎴疯嚜瀹氫箟鎵╁睍妯″潡
+      if (this.onlyCustomizeAddi) {
+        if (Object.prototype.toString.call(this.additionalModel) === "[object Array]") {
+          return this.additionalModel || [];
+        }
+        return [this.additionalModel];
+      }
+
+      // 鎻掑叆鐢ㄦ埛鑷畾涔夋墿灞曟ā鍧�
+      if (Object.prototype.toString.call(this.additionalModel) === "[object Array]") {
+        Modules.push(...this.additionalModel);
+      } else {
+        this.additionalModel && Modules.push(this.additionalModel);
+      }
+
+      // 缈昏瘧妯″潡
+      const TranslateModule = {
+        translate: ["value", customTranslate(this.translations || translationsCN)]
+      };
+      Modules.push(TranslateModule);
+
+      // 妯℃嫙娴佽浆妯″潡
+      if (this.simulation) {
+        Modules.push(tokenSimulation);
+      }
+
+      // 鏍规嵁闇�瑕佺殑娴佺▼绫诲瀷璁剧疆鎵╁睍鍏冪礌鏋勫缓妯″潡
+      // if (this.prefix === "bpmn") {
+      //   Modules.push(bpmnModdleExtension);
+      // }
+      if (this.prefix === "camunda") {
+        Modules.push(camundaModdleExtension);
+      }
+      if (this.prefix === "flowable") {
+        Modules.push(flowableModdleExtension);
+      }
+      if (this.prefix === "activiti") {
+        Modules.push(activitiModdleExtension);
+      }
+
+      return Modules;
+    },
+    moddleExtensions() {
+      const Extensions = {};
+      // 浠呬娇鐢ㄧ敤鎴疯嚜瀹氫箟妯″潡
+      if (this.onlyCustomizeModdle) {
+        return this.moddleExtension || null;
+      }
+
+      // 鎻掑叆鐢ㄦ埛鑷畾涔夋ā鍧�
+      if (this.moddleExtension) {
+        for (let key in this.moddleExtension) {
+          Extensions[key] = this.moddleExtension[key];
+        }
+      }
+
+      // 鏍规嵁闇�瑕佺殑 "娴佺▼绫诲瀷" 璁剧疆 瀵瑰簲鐨勮В鏋愭枃浠�
+      if (this.prefix === "activiti") {
+        Extensions.activiti = activitiModdleDescriptor;
+      }
+      if (this.prefix === "flowable") {
+        Extensions.flowable = flowableModdleDescriptor;
+      }
+      if (this.prefix === "camunda") {
+        Extensions.camunda = camundaModdleDescriptor;
+      }
+
+      return Extensions;
+    }
+  },
+  mounted() {
+    this.initBpmnModeler();
+    this.createNewDiagram(this.modelValue);
+    // this.$once("hook:beforeUnmount", () => {
+    //   if (this.bpmnModeler) this.bpmnModeler.destroy();
+    //   this.$emit("destroy", this.bpmnModeler);
+    //   this.bpmnModeler = null;
+    // });
+  },
+  beforeUnmount() {
+    if (this.bpmnModeler) this.bpmnModeler.destroy();
+    this.$emit("destroy", this.bpmnModeler);
+    this.bpmnModeler = null;
+  },
+  methods: {
+    onSave () {
+      return new Promise((resolve, reject) => {
+        if (this.bpmnModeler == null) {
+          reject();
+        }
+        this.bpmnModeler.saveXML({ format: true }).then(({ xml }) => {
+          this.$emit('save', xml);
+          resolve(xml);
+        });
+      })
+    },
+    initBpmnModeler() {
+      if (this.bpmnModeler) return;
+      this.bpmnModeler = new BpmnModeler({
+        container: this.$refs["bpmn-canvas"],
+        keyboard: this.keyboard ? { bindTo: document } : null,
+        additionalModules: this.additionalModules,
+        moddleExtensions: this.moddleExtensions,
+        ...this.options
+      });
+      this.$emit("init-finished", this.bpmnModeler);
+      this.initModelListeners();
+    },
+    initModelListeners() {
+      const EventBus = this.bpmnModeler.get("eventBus");
+      const that = this;
+      // 娉ㄥ唽闇�瑕佺殑鐩戝惉浜嬩欢, 灏�. 鏇挎崲涓� - , 閬垮厤瑙f瀽寮傚父
+      this.events.forEach(event => {
+        EventBus.on(event, function(eventObj) {
+          let eventName = event.replace(/\./g, "-");
+          let element = eventObj ? eventObj.element : null;
+          that.$emit(eventName, element, eventObj);
+        });
+      });
+      // 鐩戝惉鍥惧舰鏀瑰彉杩斿洖xml
+      EventBus.on("commandStack.changed", async event => {
+        try {
+          this.recoverable = this.bpmnModeler.get("commandStack").canRedo();
+          this.revocable = this.bpmnModeler.get("commandStack").canUndo();
+          let { xml } = await this.bpmnModeler.saveXML({ format: true });
+          this.$emit("commandStack-changed", event);
+          this.$emit('update:modelValue', xml);
+          this.$emit("change", xml);
+        } catch (e) {
+          console.error(`[Process Designer Warn]: ${e.message || e}`);
+        }
+      });
+      // 鐩戝惉瑙嗗浘缂╂斁鍙樺寲
+      this.bpmnModeler.on("canvas.viewbox.changed", ({ viewbox }) => {
+        this.$emit("canvas-viewbox-changed", { viewbox });
+        const { scale } = viewbox;
+        this.defaultZoom = Math.floor(scale * 100) / 100;
+      });
+    },
+    /* 鍒涘缓鏂扮殑娴佺▼鍥� */
+    async createNewDiagram(xml) {
+      // 灏嗗瓧绗︿覆杞崲鎴愬浘鏄剧ず鍑烘潵
+      let newId = this.processId || `Process_${new Date().getTime()}`;
+      let newName = this.processName || `涓氬姟娴佺▼_${new Date().getTime()}`;
+      let xmlString = xml || DefaultEmptyXML(newId, newName, this.prefix);
+      try {
+        let { warnings } = await this.bpmnModeler.importXML(xmlString);
+        if (warnings && warnings.length) {
+          warnings.forEach(warn => console.warn(warn));
+        }
+      } catch (e) {
+        console.error(`[Process Designer Warn]: ${e?.message || e}`);
+      }
+    },
+
+    // 涓嬭浇娴佺▼鍥惧埌鏈湴
+    /**
+     * @param {string} type
+     * @param {*} name
+     */
+    async downloadProcess(type, name) {
+      try {
+        const _this = this;
+        // 鎸夐渶瑕佺被鍨嬪垱寤烘枃浠跺苟涓嬭浇
+        if (type === "xml" || type === "bpmn") {
+          const { err, xml } = await this.bpmnModeler.saveXML();
+          // 璇诲彇寮傚父鏃舵姏鍑哄紓甯�
+          if (err) {
+            console.error(`[Process Designer Warn ]: ${err.message || err}`);
+          }
+          let { href, filename } = _this.setEncoded(type.toUpperCase(), name, xml);
+          downloadFunc(href, filename);
+        } else {
+          const { err, svg } = await this.bpmnModeler.saveSVG();
+          // 璇诲彇寮傚父鏃舵姏鍑哄紓甯�
+          if (err) {
+            return console.error(err);
+          }
+          let { href, filename } = _this.setEncoded("SVG", name, svg);
+          downloadFunc(href, filename);
+        }
+      } catch (e) {
+        console.error(`[Process Designer Warn ]: ${e.message || e}`);
+      }
+      // 鏂囦欢涓嬭浇鏂规硶
+      function downloadFunc(href, filename) {
+        if (href && filename) {
+          let a = document.createElement("a");
+          a.download = filename; //鎸囧畾涓嬭浇鐨勬枃浠跺悕
+          a.href = href; //  URL瀵硅薄
+          a.click(); // 妯℃嫙鐐瑰嚮
+          URL.revokeObjectURL(a.href); // 閲婃斁URL 瀵硅薄
+        }
+      }
+    },
+
+    // 鏍规嵁鎵�闇�绫诲瀷杩涜杞爜骞惰繑鍥炰笅杞藉湴鍧�
+    setEncoded(type, filename = "diagram", data) {
+      const encodedData = encodeURIComponent(data);
+      return {
+        filename: `${filename}.${type}`,
+        href: `data:application/${type === "svg" ? "text/xml" : "bpmn20-xml"};charset=UTF-8,${encodedData}`,
+        data: data
+      };
+    },
+
+    // 鍔犺浇鏈湴鏂囦欢
+    importLocalFile() {
+      const that = this;
+      const file = this.$refs.refFile.files[0];
+      const reader = new FileReader();
+      reader.readAsText(file);
+      reader.onload = function() {
+        let xmlStr = this.result;
+        that.createNewDiagram(xmlStr);
+      };
+    },
+    /* ------------------------------------------------ refs methods ------------------------------------------------------ */
+    downloadProcessAsXml() {
+      this.downloadProcess("xml");
+    },
+    downloadProcessAsBpmn() {
+      this.downloadProcess("bpmn");
+    },
+    downloadProcessAsSvg() {
+      this.downloadProcess("svg");
+    },
+    processSimulation() {
+      this.simulationStatus = !this.simulationStatus;
+      this.simulation && this.bpmnModeler.get("toggleMode").toggleMode();
+    },
+    processRedo() {
+      this.bpmnModeler.get("commandStack").redo();
+    },
+    processUndo() {
+      this.bpmnModeler.get("commandStack").undo();
+    },
+    processZoomIn(zoomStep = 0.1) {
+      let newZoom = Math.floor(this.defaultZoom * 100 + zoomStep * 100) / 100;
+      if (newZoom > 4) {
+        throw new Error("[Process Designer Warn ]: The zoom ratio cannot be greater than 4");
+      }
+      this.defaultZoom = newZoom;
+      this.bpmnModeler.get("canvas").zoom(this.defaultZoom);
+    },
+    processZoomOut(zoomStep = 0.1) {
+      let newZoom = Math.floor(this.defaultZoom * 100 - zoomStep * 100) / 100;
+      if (newZoom < 0.2) {
+        throw new Error("[Process Designer Warn ]: The zoom ratio cannot be less than 0.2");
+      }
+      this.defaultZoom = newZoom;
+      this.bpmnModeler.get("canvas").zoom(this.defaultZoom);
+    },
+    processZoomTo(newZoom = 1) {
+      if (newZoom < 0.2) {
+        throw new Error("[Process Designer Warn ]: The zoom ratio cannot be less than 0.2");
+      }
+      if (newZoom > 4) {
+        throw new Error("[Process Designer Warn ]: The zoom ratio cannot be greater than 4");
+      }
+      this.defaultZoom = newZoom;
+      this.bpmnModeler.get("canvas").zoom(newZoom);
+    },
+    processReZoom() {
+      this.defaultZoom = 1;
+      this.bpmnModeler.get("canvas").zoom("fit-viewport", "auto");
+    },
+    processRestart() {
+      this.recoverable = false;
+      this.revocable = false;
+      this.createNewDiagram(null);
+    },
+    elementsAlign(align) {
+      const Align = this.bpmnModeler.get("alignElements");
+      const Selection = this.bpmnModeler.get("selection");
+      const SelectedElements = Selection.get();
+      if (!SelectedElements || SelectedElements.length <= 1) {
+        this.$message.warning("璇锋寜浣� Ctrl 閿�夋嫨澶氫釜鍏冪礌瀵归綈");
+        return;
+      }
+      this.$confirm("鑷姩瀵归綈鍙兘閫犳垚鍥惧舰鍙樺舰锛屾槸鍚︾户缁紵", "璀﹀憡", {
+        confirmButtonText: "纭畾",
+        cancelButtonText: "鍙栨秷",
+        type: "warning"
+      }).then(() => Align.trigger(SelectedElements, align));
+    },
+    /*-----------------------------    鏂规硶缁撴潫     ---------------------------------*/
+    previewProcessXML() {
+      this.bpmnModeler.saveXML({ format: true }).then(({ xml }) => {
+        this.previewResult = xml;
+        this.previewType = 'xml';
+        this.cmOptions.mode = 'xml'
+        this.previewModelVisible = true;
+      });
+    },
+    previewProcessJson() {
+      // this.bpmnModeler.saveXML({ format: true }).then(({ xml }) => {
+      //   this.previewResult = convert.xml2json(xml, { spaces: 2 });
+      //   this.previewType = "json";
+      //   this.previewModelVisible = true;
+      // });
+      const newConvert = new X2JS();
+      this.bpmnModeler.saveXML({ format: true }).then(({ xml }) => {
+        const { definitions } = newConvert.xml2js(xml);
+        if (definitions) {
+          this.previewResult = JSON.stringify(definitions, null, 4);
+        } else {
+          this.previewResult = "";
+        }
+
+        this.previewType = "json";
+        this.previewModelVisible = true;
+      });
+    }
+  }
+};
+</script>
diff --git a/ruoyi-ui/apps/web-antd/src/package/designer/index.js b/ruoyi-ui/apps/web-antd/src/package/designer/index.js
new file mode 100644
index 0000000..333d1bc
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/designer/index.js
@@ -0,0 +1,7 @@
+import MyProcessDesigner from './ProcessDesigner.vue';
+
+MyProcessDesigner.install = function(Vue) {
+  Vue.component(MyProcessDesigner.name, MyProcessDesigner);
+};
+
+export default MyProcessDesigner;
diff --git a/ruoyi-ui/apps/web-antd/src/package/designer/plugins/content-pad/contentPadProvider.js b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/content-pad/contentPadProvider.js
new file mode 100644
index 0000000..d3eae19
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/content-pad/contentPadProvider.js
@@ -0,0 +1,390 @@
+import { assign, forEach, isArray } from 'min-dash';
+
+import { is } from 'bpmn-js/lib/util/ModelUtil';
+
+import { isExpanded, isEventSubProcess } from 'bpmn-js/lib/util/DiUtil';
+
+import { isAny } from 'bpmn-js/lib/features/modeling/util/ModelingUtil';
+
+import { getChildLanes } from 'bpmn-js/lib/features/modeling/util/LaneUtil';
+
+import { hasPrimaryModifier } from 'diagram-js/lib/util/Mouse';
+
+/**
+ * A provider for BPMN 2.0 elements context pad
+ */
+export default function ContextPadProvider(
+  config,
+  injector,
+  eventBus,
+  contextPad,
+  modeling,
+  elementFactory,
+  connect,
+  create,
+  popupMenu,
+  canvas,
+  rules,
+  translate,
+  elementRegistry
+) {
+  config = config || {};
+
+  contextPad.registerProvider(this);
+
+  this._contextPad = contextPad;
+
+  this._modeling = modeling;
+
+  this._elementFactory = elementFactory;
+  this._connect = connect;
+  this._create = create;
+  this._popupMenu = popupMenu;
+  this._canvas = canvas;
+  this._rules = rules;
+  this._translate = translate;
+
+  if (config.autoPlace !== false) {
+    this._autoPlace = injector.get('autoPlace', false);
+  }
+
+  eventBus.on('create.end', 250, function(event) {
+    var context = event.context;
+    var shape = context.shape;
+
+    if (!hasPrimaryModifier(event) || !contextPad.isOpen(shape)) {
+      return;
+    }
+
+    var entries = contextPad.getEntries(shape);
+
+    if (entries.replace) {
+      entries.replace.action.click(event, shape);
+    }
+  });
+}
+
+ContextPadProvider.$inject = [
+  'config.contextPad',
+  'injector',
+  'eventBus',
+  'contextPad',
+  'modeling',
+  'elementFactory',
+  'connect',
+  'create',
+  'popupMenu',
+  'canvas',
+  'rules',
+  'translate',
+  'elementRegistry'
+];
+
+ContextPadProvider.prototype.getContextPadEntries = function(element) {
+  var contextPad = this._contextPad;
+  var modeling = this._modeling;
+  var elementFactory = this._elementFactory;
+  var connect = this._connect;
+  var create = this._create;
+  var popupMenu = this._popupMenu;
+  var canvas = this._canvas;
+  var rules = this._rules;
+  var autoPlace = this._autoPlace;
+  var translate = this._translate;
+
+  var actions = {};
+
+  if (element.type === 'label') {
+    return actions;
+  }
+
+  var businessObject = element.businessObject;
+
+  function startConnect(event, element) {
+    connect.start(event, element);
+  }
+
+  function removeElement() {
+    modeling.removeElements([element]);
+  }
+
+  function getReplaceMenuPosition(element) {
+    var Y_OFFSET = 5;
+
+    var diagramContainer = canvas.getContainer();
+    var pad = contextPad.getPad(element).html;
+
+    var diagramRect = diagramContainer.getBoundingClientRect();
+    var padRect = pad.getBoundingClientRect();
+
+    var top = padRect.top - diagramRect.top;
+    var left = padRect.left - diagramRect.left;
+
+    var pos = {
+      x: left,
+      y: top + padRect.height + Y_OFFSET
+    };
+
+    return pos;
+  }
+
+  /**
+   * Create an append action
+   *
+   * @param {string} type
+   * @param {string} className
+   * @param {string} [title]
+   * @param {Object} [options]
+   *
+   * @return {Object} descriptor
+   */
+  function appendAction(type, className, title, options) {
+    if (typeof title !== 'string') {
+      options = title;
+      title = translate('Append {type}', { type: type.replace(/^bpmn:/, '') });
+    }
+
+    function appendStart(event, element) {
+      var shape = elementFactory.createShape(assign({ type: type }, options));
+      create.start(event, shape, {
+        source: element
+      });
+    }
+
+    var append = autoPlace
+      ? function(event, element) {
+        var shape = elementFactory.createShape(assign({ type: type }, options));
+
+        autoPlace.append(element, shape);
+      }
+      : appendStart;
+
+    return {
+      group: 'model',
+      className: className,
+      title: title,
+      action: {
+        dragstart: appendStart,
+        click: append
+      }
+    };
+  }
+
+  function splitLaneHandler(count) {
+    return function(event, element) {
+      // actual split
+      modeling.splitLane(element, count);
+
+      // refresh context pad after split to
+      // get rid of split icons
+      contextPad.open(element, true);
+    };
+  }
+
+  if (isAny(businessObject, ['bpmn:Lane', 'bpmn:Participant']) && isExpanded(businessObject)) {
+    var childLanes = getChildLanes(element);
+
+    assign(actions, {
+      'lane-insert-above': {
+        group: 'lane-insert-above',
+        className: 'bpmn-icon-lane-insert-above',
+        title: translate('Add Lane above'),
+        action: {
+          click: function(event, element) {
+            modeling.addLane(element, 'top');
+          }
+        }
+      }
+    });
+
+    if (childLanes.length < 2) {
+      if (element.height >= 120) {
+        assign(actions, {
+          'lane-divide-two': {
+            group: 'lane-divide',
+            className: 'bpmn-icon-lane-divide-two',
+            title: translate('Divide into two Lanes'),
+            action: {
+              click: splitLaneHandler(2)
+            }
+          }
+        });
+      }
+
+      if (element.height >= 180) {
+        assign(actions, {
+          'lane-divide-three': {
+            group: 'lane-divide',
+            className: 'bpmn-icon-lane-divide-three',
+            title: translate('Divide into three Lanes'),
+            action: {
+              click: splitLaneHandler(3)
+            }
+          }
+        });
+      }
+    }
+
+    assign(actions, {
+      'lane-insert-below': {
+        group: 'lane-insert-below',
+        className: 'bpmn-icon-lane-insert-below',
+        title: translate('Add Lane below'),
+        action: {
+          click: function(event, element) {
+            modeling.addLane(element, 'bottom');
+          }
+        }
+      }
+    });
+  }
+
+  if (is(businessObject, 'bpmn:FlowNode')) {
+    if (is(businessObject, 'bpmn:EventBasedGateway')) {
+      assign(actions, {
+        'append.receive-task': appendAction('bpmn:ReceiveTask', 'bpmn-icon-receive-task', translate('Append ReceiveTask')),
+        'append.message-intermediate-event': appendAction(
+          'bpmn:IntermediateCatchEvent',
+          'bpmn-icon-intermediate-event-catch-message',
+          translate('Append MessageIntermediateCatchEvent'),
+          { eventDefinitionType: 'bpmn:MessageEventDefinition' }
+        ),
+        'append.timer-intermediate-event': appendAction(
+          'bpmn:IntermediateCatchEvent',
+          'bpmn-icon-intermediate-event-catch-timer',
+          translate('Append TimerIntermediateCatchEvent'),
+          { eventDefinitionType: 'bpmn:TimerEventDefinition' }
+        ),
+        'append.condition-intermediate-event': appendAction(
+          'bpmn:IntermediateCatchEvent',
+          'bpmn-icon-intermediate-event-catch-condition',
+          translate('Append ConditionIntermediateCatchEvent'),
+          { eventDefinitionType: 'bpmn:ConditionalEventDefinition' }
+        ),
+        'append.signal-intermediate-event': appendAction(
+          'bpmn:IntermediateCatchEvent',
+          'bpmn-icon-intermediate-event-catch-signal',
+          translate('Append SignalIntermediateCatchEvent'),
+          { eventDefinitionType: 'bpmn:SignalEventDefinition' }
+        )
+      });
+    } else if (isEventType(businessObject, 'bpmn:BoundaryEvent', 'bpmn:CompensateEventDefinition')) {
+      assign(actions, {
+        'append.compensation-activity': appendAction('bpmn:Task', 'bpmn-icon-task', translate('Append compensation activity'), {
+          isForCompensation: true
+        })
+      });
+    } else if (
+      !is(businessObject, 'bpmn:EndEvent') &&
+      !businessObject.isForCompensation &&
+      !isEventType(businessObject, 'bpmn:IntermediateThrowEvent', 'bpmn:LinkEventDefinition') &&
+      !isEventSubProcess(businessObject)
+    ) {
+      assign(actions, {
+        'append.end-event': appendAction('bpmn:EndEvent', 'bpmn-icon-end-event-none', translate('Append EndEvent')),
+        'append.gateway': appendAction('bpmn:ExclusiveGateway', 'bpmn-icon-gateway-none', translate('Append Gateway')),
+        'append.append-task': appendAction('bpmn:UserTask', 'bpmn-icon-user-task', translate('Append Task')),
+        'append.intermediate-event': appendAction(
+          'bpmn:IntermediateThrowEvent',
+          'bpmn-icon-intermediate-event-none',
+          translate('Append Intermediate/Boundary Event')
+        )
+      });
+    }
+  }
+
+  if (!popupMenu.isEmpty(element, 'bpmn-replace')) {
+    // Replace menu entry
+    assign(actions, {
+      replace: {
+        group: 'edit',
+        className: 'bpmn-icon-screw-wrench',
+        title: translate('Change type'),
+        action: {
+          click: function(event, element) {
+            var position = assign(getReplaceMenuPosition(element), {
+              cursor: { x: event.x, y: event.y }
+            });
+
+            popupMenu.open(element, 'bpmn-replace', position);
+          }
+        }
+      }
+    });
+  }
+
+  if (isAny(businessObject, ['bpmn:FlowNode', 'bpmn:InteractionNode', 'bpmn:DataObjectReference', 'bpmn:DataStoreReference'])) {
+    assign(actions, {
+      'append.text-annotation': appendAction('bpmn:TextAnnotation', 'bpmn-icon-text-annotation'),
+
+      connect: {
+        group: 'connect',
+        className: 'bpmn-icon-connection-multi',
+        title: translate('Connect using ' + (businessObject.isForCompensation ? '' : 'Sequence/MessageFlow or ') + 'Association'),
+        action: {
+          click: startConnect,
+          dragstart: startConnect
+        }
+      }
+    });
+  }
+
+  if (isAny(businessObject, ['bpmn:DataObjectReference', 'bpmn:DataStoreReference'])) {
+    assign(actions, {
+      connect: {
+        group: 'connect',
+        className: 'bpmn-icon-connection-multi',
+        title: translate('Connect using DataInputAssociation'),
+        action: {
+          click: startConnect,
+          dragstart: startConnect
+        }
+      }
+    });
+  }
+
+  if (is(businessObject, 'bpmn:Group')) {
+    assign(actions, {
+      'append.text-annotation': appendAction('bpmn:TextAnnotation', 'bpmn-icon-text-annotation')
+    });
+  }
+
+  // delete element entry, only show if allowed by rules
+  var deleteAllowed = rules.allowed('elements.delete', { elements: [element] });
+
+  if (isArray(deleteAllowed)) {
+    // was the element returned as a deletion candidate?
+    deleteAllowed = deleteAllowed[0] === element;
+  }
+
+  if (deleteAllowed) {
+    assign(actions, {
+      delete: {
+        group: 'edit',
+        className: 'bpmn-icon-trash',
+        title: translate('Remove'),
+        action: {
+          click: removeElement
+        }
+      }
+    });
+  }
+
+  return actions;
+};
+
+// helpers /////////
+
+function isEventType(eventBo, type, definition) {
+  var isType = eventBo.$instanceOf(type);
+  var isDefinition = false;
+
+  var definitions = eventBo.eventDefinitions || [];
+  forEach(definitions, function(def) {
+    if (def.$type === definition) {
+      isDefinition = true;
+    }
+  });
+
+  return isType && isDefinition;
+}
diff --git a/ruoyi-ui/apps/web-antd/src/package/designer/plugins/content-pad/index.js b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/content-pad/index.js
new file mode 100644
index 0000000..ad826f1
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/content-pad/index.js
@@ -0,0 +1,6 @@
+import CustomContextPadProvider from './contentPadProvider';
+
+export default {
+  __init__: ['contextPadProvider'],
+  contextPadProvider: ['type', CustomContextPadProvider]
+};
diff --git a/ruoyi-ui/apps/web-antd/src/package/designer/plugins/defaultEmpty.js b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/defaultEmpty.js
new file mode 100644
index 0000000..46dd4c1
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/defaultEmpty.js
@@ -0,0 +1,24 @@
+export default (key, name, type) => {
+  if (!type) type = 'camunda';
+  const TYPE_TARGET = {
+    activiti: 'http://activiti.org/bpmn',
+    camunda: 'http://bpmn.io/schema/bpmn',
+    flowable: 'http://flowable.org/bpmn'
+  };
+  return `<?xml version="1.0" encoding="UTF-8"?>
+<bpmn2:definitions 
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL"
+  xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
+  xmlns:dc="http://www.omg.org/spec/DD/20100524/DC"
+  xmlns:di="http://www.omg.org/spec/DD/20100524/DI"
+  id="diagram_${key}"
+  targetNamespace="${TYPE_TARGET[type]}">
+  <bpmn2:process id="${key}" name="${name}" isExecutable="true">
+  </bpmn2:process>
+  <bpmndi:BPMNDiagram id="BPMNDiagram_1">
+    <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="${key}">
+    </bpmndi:BPMNPlane>
+  </bpmndi:BPMNDiagram>
+</bpmn2:definitions>`;
+};
diff --git a/ruoyi-ui/apps/web-antd/src/package/designer/plugins/descriptor/activitiDescriptor.json b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/descriptor/activitiDescriptor.json
new file mode 100644
index 0000000..9daed50
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/descriptor/activitiDescriptor.json
@@ -0,0 +1,1071 @@
+{
+  "name": "Activiti",
+  "uri": "http://activiti.org/bpmn",
+  "prefix": "activiti",
+  "xml": {
+    "tagAlias": "lowerCase"
+  },
+  "associations": [],
+  "types": [
+    {
+      "name": "Definitions",
+      "isAbstract": true,
+      "extends": [
+        "bpmn:Definitions"
+      ],
+      "properties": [
+        {
+          "name": "diagramRelationId",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "InOutBinding",
+      "superClass": [
+        "Element"
+      ],
+      "isAbstract": true,
+      "properties": [
+        {
+          "name": "source",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "sourceExpression",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "target",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "businessKey",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "local",
+          "isAttr": true,
+          "type": "Boolean",
+          "default": false
+        },
+        {
+          "name": "variables",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "In",
+      "superClass": [
+        "InOutBinding"
+      ],
+      "meta": {
+        "allowedIn": [
+          "bpmn:CallActivity"
+        ]
+      }
+    },
+    {
+      "name": "Out",
+      "superClass": [
+        "InOutBinding"
+      ],
+      "meta": {
+        "allowedIn": [
+          "bpmn:CallActivity"
+        ]
+      }
+    },
+    {
+      "name": "AsyncCapable",
+      "isAbstract": true,
+      "extends": [
+        "bpmn:Activity",
+        "bpmn:Gateway",
+        "bpmn:Event"
+      ],
+      "properties": [
+        {
+          "name": "async",
+          "isAttr": true,
+          "type": "Boolean",
+          "default": false
+        },
+        {
+          "name": "asyncBefore",
+          "isAttr": true,
+          "type": "Boolean",
+          "default": false
+        },
+        {
+          "name": "asyncAfter",
+          "isAttr": true,
+          "type": "Boolean",
+          "default": false
+        },
+        {
+          "name": "exclusive",
+          "isAttr": true,
+          "type": "Boolean",
+          "default": true
+        }
+      ]
+    },
+    {
+      "name": "JobPriorized",
+      "isAbstract": true,
+      "extends": [
+        "bpmn:Process",
+        "activiti:AsyncCapable"
+      ],
+      "properties": [
+        {
+          "name": "jobPriority",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "SignalEventDefinition",
+      "isAbstract": true,
+      "extends": [
+        "bpmn:SignalEventDefinition"
+      ],
+      "properties": [
+        {
+          "name": "async",
+          "isAttr": true,
+          "type": "Boolean",
+          "default": false
+        }
+      ]
+    },
+    {
+      "name": "ErrorEventDefinition",
+      "isAbstract": true,
+      "extends": [
+        "bpmn:ErrorEventDefinition"
+      ],
+      "properties": [
+        {
+          "name": "errorCodeVariable",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "errorMessageVariable",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "Error",
+      "isAbstract": true,
+      "extends": [
+        "bpmn:Error"
+      ],
+      "properties": [
+        {
+          "name": "activiti:errorMessage",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "PotentialStarter",
+      "superClass": [
+        "Element"
+      ],
+      "properties": [
+        {
+          "name": "resourceAssignmentExpression",
+          "type": "bpmn:ResourceAssignmentExpression"
+        }
+      ]
+    },
+    {
+      "name": "FormSupported",
+      "isAbstract": true,
+      "extends": [
+        "bpmn:StartEvent",
+        "bpmn:UserTask"
+      ],
+      "properties": [
+        {
+          "name": "formHandlerClass",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "formKey",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "TemplateSupported",
+      "isAbstract": true,
+      "extends": [
+        "bpmn:Process",
+        "bpmn:FlowElement"
+      ],
+      "properties": [
+        {
+          "name": "modelerTemplate",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "Initiator",
+      "isAbstract": true,
+      "extends": [ "bpmn:StartEvent" ],
+      "properties": [
+        {
+          "name": "initiator",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "ScriptTask",
+      "isAbstract": true,
+      "extends": [
+        "bpmn:ScriptTask"
+      ],
+      "properties": [
+        {
+          "name": "resultVariable",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "resource",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "Process",
+      "isAbstract": true,
+      "extends": [
+        "bpmn:Process"
+      ],
+      "properties": [
+        {
+          "name": "candidateStarterGroups",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "candidateStarterUsers",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "versionTag",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "historyTimeToLive",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "isStartableInTasklist",
+          "isAttr": true,
+          "type": "Boolean",
+          "default": true
+        },
+        {
+          "name":"executionListener",
+          "isAbstract": true,
+          "type":"Expression"
+        }
+      ]
+    },
+    {
+      "name": "EscalationEventDefinition",
+      "isAbstract": true,
+      "extends": [
+        "bpmn:EscalationEventDefinition"
+      ],
+      "properties": [
+        {
+          "name": "escalationCodeVariable",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "FormalExpression",
+      "isAbstract": true,
+      "extends": [
+        "bpmn:FormalExpression"
+      ],
+      "properties": [
+        {
+          "name": "resource",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "multiinstance_type",
+      "superClass":[
+        "Element"
+      ]
+    },
+    {
+      "name": "multiinstance_condition",
+      "superClass":[
+        "Element"
+      ]
+    },
+    {
+      "name": "Assignable",
+      "extends": [ "bpmn:UserTask" ],
+      "properties": [
+        {
+          "name": "assignee",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "candidateUsers",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "candidateGroups",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "dueDate",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "followUpDate",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "priority",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "multiinstance_condition",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "CallActivity",
+      "extends": [ "bpmn:CallActivity" ],
+      "properties": [
+        {
+          "name": "calledElementBinding",
+          "isAttr": true,
+          "type": "String",
+          "default": "latest"
+        },
+        {
+          "name": "calledElementVersion",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "calledElementVersionTag",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "calledElementTenantId",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "caseRef",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "caseBinding",
+          "isAttr": true,
+          "type": "String",
+          "default": "latest"
+        },
+        {
+          "name": "caseVersion",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "caseTenantId",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "variableMappingClass",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "variableMappingDelegateExpression",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "ServiceTaskLike",
+      "extends": [
+        "bpmn:ServiceTask",
+        "bpmn:BusinessRuleTask",
+        "bpmn:SendTask",
+        "bpmn:MessageEventDefinition"
+      ],
+      "properties": [
+        {
+          "name": "expression",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "class",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "delegateExpression",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "resultVariable",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "DmnCapable",
+      "extends": [
+        "bpmn:BusinessRuleTask"
+      ],
+      "properties": [
+        {
+          "name": "decisionRef",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "decisionRefBinding",
+          "isAttr": true,
+          "type": "String",
+          "default": "latest"
+        },
+        {
+          "name": "decisionRefVersion",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "mapDecisionResult",
+          "isAttr": true,
+          "type": "String",
+          "default": "resultList"
+        },
+        {
+          "name": "decisionRefTenantId",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "ExternalCapable",
+      "extends": [
+        "activiti:ServiceTaskLike"
+      ],
+      "properties": [
+        {
+          "name": "type",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "topic",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "TaskPriorized",
+      "extends": [
+        "bpmn:Process",
+        "activiti:ExternalCapable"
+      ],
+      "properties": [
+        {
+          "name": "taskPriority",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "Properties",
+      "superClass": [
+        "Element"
+      ],
+      "meta": {
+        "allowedIn": [ "*" ]
+      },
+      "properties": [
+        {
+          "name": "values",
+          "type": "Property",
+          "isMany": true
+        }
+      ]
+    },
+    {
+      "name": "Property",
+      "superClass": [
+        "Element"
+      ],
+      "properties": [
+        {
+          "name": "id",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "name",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "value",
+          "type": "String",
+          "isAttr": true
+        }
+      ]
+    },
+    {
+      "name": "Connector",
+      "superClass": [
+        "Element"
+      ],
+      "meta": {
+        "allowedIn": [
+          "activiti:ServiceTaskLike"
+        ]
+      },
+      "properties": [
+        {
+          "name": "inputOutput",
+          "type": "InputOutput"
+        },
+        {
+          "name": "connectorId",
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "InputOutput",
+      "superClass": [
+        "Element"
+      ],
+      "meta": {
+        "allowedIn": [
+          "bpmn:FlowNode",
+          "activiti:Connector"
+        ]
+      },
+      "properties": [
+        {
+          "name": "inputOutput",
+          "type": "InputOutput"
+        },
+        {
+          "name": "connectorId",
+          "type": "String"
+        },
+        {
+          "name": "inputParameters",
+          "isMany": true,
+          "type": "InputParameter"
+        },
+        {
+          "name": "outputParameters",
+          "isMany": true,
+          "type": "OutputParameter"
+        }
+      ]
+    },
+    {
+      "name": "InputOutputParameter",
+      "properties": [
+        {
+          "name": "name",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "value",
+          "isBody": true,
+          "type": "String"
+        },
+        {
+          "name": "definition",
+          "type": "InputOutputParameterDefinition"
+        }
+      ]
+    },
+    {
+      "name": "InputOutputParameterDefinition",
+      "isAbstract": true
+    },
+    {
+      "name": "List",
+      "superClass": [ "InputOutputParameterDefinition" ],
+      "properties": [
+        {
+          "name": "items",
+          "isMany": true,
+          "type": "InputOutputParameterDefinition"
+        }
+      ]
+    },
+    {
+      "name": "Map",
+      "superClass": [ "InputOutputParameterDefinition" ],
+      "properties": [
+        {
+          "name": "entries",
+          "isMany": true,
+          "type": "Entry"
+        }
+      ]
+    },
+    {
+      "name": "Entry",
+      "properties": [
+        {
+          "name": "key",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "value",
+          "isBody": true,
+          "type": "String"
+        },
+        {
+          "name": "definition",
+          "type": "InputOutputParameterDefinition"
+        }
+      ]
+    },
+    {
+      "name": "Value",
+      "superClass": [
+        "InputOutputParameterDefinition"
+      ],
+      "properties": [
+        {
+          "name": "id",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "name",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "value",
+          "isBody": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "Script",
+      "superClass": [ "InputOutputParameterDefinition" ],
+      "properties": [
+        {
+          "name": "scriptFormat",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "resource",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "value",
+          "isBody": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "Field",
+      "superClass": [ "Element" ],
+      "meta": {
+        "allowedIn": [
+          "activiti:ServiceTaskLike",
+          "activiti:ExecutionListener",
+          "activiti:TaskListener"
+        ]
+      },
+      "properties": [
+        {
+          "name": "name",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "expression",
+          "type": "String"
+        },
+        {
+          "name": "stringValue",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "string",
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "InputParameter",
+      "superClass": [ "InputOutputParameter" ]
+    },
+    {
+      "name": "OutputParameter",
+      "superClass": [ "InputOutputParameter" ]
+    },
+    {
+      "name": "Collectable",
+      "isAbstract": true,
+      "extends": [ "bpmn:MultiInstanceLoopCharacteristics" ],
+      "superClass": [ "activiti:AsyncCapable" ],
+      "properties": [
+        {
+          "name": "collection",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "elementVariable",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "FailedJobRetryTimeCycle",
+      "superClass": [ "Element" ],
+      "meta": {
+        "allowedIn": [
+          "activiti:AsyncCapable",
+          "bpmn:MultiInstanceLoopCharacteristics"
+        ]
+      },
+      "properties": [
+        {
+          "name": "body",
+          "isBody": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "ExecutionListener",
+      "superClass": [ "Element" ],
+      "meta": {
+        "allowedIn": [
+          "bpmn:Task",
+          "bpmn:ServiceTask",
+          "bpmn:UserTask",
+          "bpmn:BusinessRuleTask",
+          "bpmn:ScriptTask",
+          "bpmn:ReceiveTask",
+          "bpmn:ManualTask",
+          "bpmn:ExclusiveGateway",
+          "bpmn:SequenceFlow",
+          "bpmn:ParallelGateway",
+          "bpmn:InclusiveGateway",
+          "bpmn:EventBasedGateway",
+          "bpmn:StartEvent",
+          "bpmn:IntermediateCatchEvent",
+          "bpmn:IntermediateThrowEvent",
+          "bpmn:EndEvent",
+          "bpmn:BoundaryEvent",
+          "bpmn:CallActivity",
+          "bpmn:SubProcess",
+          "bpmn:Process"
+        ]
+      },
+      "properties": [
+        {
+          "name": "expression",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "class",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "delegateExpression",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "event",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "script",
+          "type": "Script"
+        },
+        {
+          "name": "fields",
+          "type": "Field",
+          "isMany": true
+        }
+      ]
+    },
+    {
+      "name": "TaskListener",
+      "superClass": [ "Element" ],
+      "meta": {
+        "allowedIn": [
+          "bpmn:UserTask"
+        ]
+      },
+      "properties": [
+        {
+          "name": "expression",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "class",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "delegateExpression",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "event",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "script",
+          "type": "Script"
+        },
+        {
+          "name": "fields",
+          "type": "Field",
+          "isMany": true
+        }
+      ]
+    },
+    {
+      "name": "FormProperty",
+      "superClass": [ "Element" ],
+      "meta": {
+        "allowedIn": [
+          "bpmn:StartEvent",
+          "bpmn:UserTask"
+        ]
+      },
+      "properties": [
+        {
+          "name": "id",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "name",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "type",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "required",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "readable",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "writable",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "variable",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "expression",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "datePattern",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "default",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "values",
+          "type": "Value",
+          "isMany": true
+        }
+      ]
+    },
+    {
+      "name": "FormProperty",
+      "superClass": [ "Element" ],
+      "properties": [
+        {
+          "name": "id",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "label",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "type",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "datePattern",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "defaultValue",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "properties",
+          "type": "Properties"
+        },
+        {
+          "name": "validation",
+          "type": "Validation"
+        },
+        {
+          "name": "values",
+          "type": "Value",
+          "isMany": true
+        }
+      ]
+    },
+    {
+      "name": "Validation",
+      "superClass": [ "Element" ],
+      "properties": [
+        {
+          "name": "constraints",
+          "type": "Constraint",
+          "isMany": true
+        }
+      ]
+    },
+    {
+      "name": "Constraint",
+      "superClass": [ "Element" ],
+      "properties": [
+        {
+          "name": "name",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "config",
+          "type": "String",
+          "isAttr": true
+        }
+      ]
+    },
+    {
+      "name": "ConditionalEventDefinition",
+      "isAbstract": true,
+      "extends": [
+        "bpmn:ConditionalEventDefinition"
+      ],
+      "properties": [
+        {
+          "name": "variableName",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "variableEvent",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    }
+  ],
+  "emumerations": [ ]
+}
diff --git a/ruoyi-ui/apps/web-antd/src/package/designer/plugins/descriptor/camundaDescriptor.json b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/descriptor/camundaDescriptor.json
new file mode 100644
index 0000000..a57dbe6
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/descriptor/camundaDescriptor.json
@@ -0,0 +1,1087 @@
+{
+  "name": "Camunda",
+  "uri": "http://camunda.org/schema/1.0/bpmn",
+  "prefix": "camunda",
+  "xml": {
+    "tagAlias": "lowerCase"
+  },
+  "associations": [],
+  "types": [
+    {
+      "name": "Definitions",
+      "isAbstract": true,
+      "extends": [
+        "bpmn:Definitions"
+      ],
+      "properties": [
+        {
+          "name": "diagramRelationId",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "InOutBinding",
+      "superClass": [
+        "Element"
+      ],
+      "isAbstract": true,
+      "properties": [
+        {
+          "name": "source",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "sourceExpression",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "target",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "businessKey",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "local",
+          "isAttr": true,
+          "type": "Boolean",
+          "default": false
+        },
+        {
+          "name": "variables",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "In",
+      "superClass": [
+        "InOutBinding"
+      ],
+      "meta": {
+        "allowedIn": [
+          "bpmn:CallActivity",
+          "bpmn:SignalEventDefinition"
+        ]
+      }
+    },
+    {
+      "name": "Out",
+      "superClass": [
+        "InOutBinding"
+      ],
+      "meta": {
+        "allowedIn": [
+          "bpmn:CallActivity"
+        ]
+      }
+    },
+    {
+      "name": "AsyncCapable",
+      "isAbstract": true,
+      "extends": [
+        "bpmn:Activity",
+        "bpmn:Gateway",
+        "bpmn:Event"
+      ],
+      "properties": [
+        {
+          "name": "async",
+          "isAttr": true,
+          "type": "Boolean",
+          "default": false
+        },
+        {
+          "name": "asyncBefore",
+          "isAttr": true,
+          "type": "Boolean",
+          "default": false
+        },
+        {
+          "name": "asyncAfter",
+          "isAttr": true,
+          "type": "Boolean",
+          "default": false
+        },
+        {
+          "name": "exclusive",
+          "isAttr": true,
+          "type": "Boolean",
+          "default": true
+        }
+      ]
+    },
+    {
+      "name": "JobPriorized",
+      "isAbstract": true,
+      "extends": [
+        "bpmn:Process",
+        "camunda:AsyncCapable"
+      ],
+      "properties": [
+        {
+          "name": "jobPriority",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "SignalEventDefinition",
+      "isAbstract": true,
+      "extends": [
+        "bpmn:SignalEventDefinition"
+      ],
+      "properties": [
+        {
+          "name": "async",
+          "isAttr": true,
+          "type": "Boolean",
+          "default": false
+        }
+      ]
+    },
+    {
+      "name": "ErrorEventDefinition",
+      "isAbstract": true,
+      "extends": [
+        "bpmn:ErrorEventDefinition"
+      ],
+      "properties": [
+        {
+          "name": "errorCodeVariable",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "errorMessageVariable",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "Error",
+      "isAbstract": true,
+      "extends": [
+        "bpmn:Error"
+      ],
+      "properties": [
+        {
+          "name": "camunda:errorMessage",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "PotentialStarter",
+      "superClass": [
+        "Element"
+      ],
+      "properties": [
+        {
+          "name": "resourceAssignmentExpression",
+          "type": "bpmn:ResourceAssignmentExpression"
+        }
+      ]
+    },
+    {
+      "name": "FormSupported",
+      "isAbstract": true,
+      "extends": [
+        "bpmn:StartEvent",
+        "bpmn:UserTask"
+      ],
+      "properties": [
+        {
+          "name": "formHandlerClass",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "formKey",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "TemplateSupported",
+      "isAbstract": true,
+      "extends": [
+        "bpmn:Process",
+        "bpmn:FlowElement"
+      ],
+      "properties": [
+        {
+          "name": "modelerTemplate",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "modelerTemplateVersion",
+          "isAttr": true,
+          "type": "Integer"
+        }
+      ]
+    },
+    {
+      "name": "Initiator",
+      "isAbstract": true,
+      "extends": [ "bpmn:StartEvent" ],
+      "properties": [
+        {
+          "name": "initiator",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "ScriptTask",
+      "isAbstract": true,
+      "extends": [
+        "bpmn:ScriptTask"
+      ],
+      "properties": [
+        {
+          "name": "resultVariable",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "resource",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "Process",
+      "isAbstract": true,
+      "extends": [
+        "bpmn:Process"
+      ],
+      "properties": [
+        {
+          "name": "candidateStarterGroups",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "candidateStarterUsers",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "versionTag",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "historyTimeToLive",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "isStartableInTasklist",
+          "isAttr": true,
+          "type": "Boolean",
+          "default": true
+        }
+      ]
+    },
+    {
+      "name": "EscalationEventDefinition",
+      "isAbstract": true,
+      "extends": [
+        "bpmn:EscalationEventDefinition"
+      ],
+      "properties": [
+        {
+          "name": "escalationCodeVariable",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "FormalExpression",
+      "isAbstract": true,
+      "extends": [
+        "bpmn:FormalExpression"
+      ],
+      "properties": [
+        {
+          "name": "resource",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "Assignable",
+      "extends": [ "bpmn:UserTask" ],
+      "properties": [
+        {
+          "name": "assignee",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "candidateUsers",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "candidateGroups",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "dueDate",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "followUpDate",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "priority",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "CallActivity",
+      "extends": [ "bpmn:CallActivity" ],
+      "properties": [
+        {
+          "name": "calledElementBinding",
+          "isAttr": true,
+          "type": "String",
+          "default": "latest"
+        },
+        {
+          "name": "calledElementVersion",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "calledElementVersionTag",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "calledElementTenantId",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "caseRef",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "caseBinding",
+          "isAttr": true,
+          "type": "String",
+          "default": "latest"
+        },
+        {
+          "name": "caseVersion",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "caseTenantId",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "variableMappingClass",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "variableMappingDelegateExpression",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "ServiceTaskLike",
+      "extends": [
+        "bpmn:ServiceTask",
+        "bpmn:BusinessRuleTask",
+        "bpmn:SendTask",
+        "bpmn:MessageEventDefinition"
+      ],
+      "properties": [
+        {
+          "name": "expression",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "class",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "delegateExpression",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "resultVariable",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "DmnCapable",
+      "extends": [
+        "bpmn:BusinessRuleTask"
+      ],
+      "properties": [
+        {
+          "name": "decisionRef",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "decisionRefBinding",
+          "isAttr": true,
+          "type": "String",
+          "default": "latest"
+        },
+        {
+          "name": "decisionRefVersion",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "mapDecisionResult",
+          "isAttr": true,
+          "type": "String",
+          "default": "resultList"
+        },
+        {
+          "name": "decisionRefTenantId",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "ExternalCapable",
+      "extends": [
+        "camunda:ServiceTaskLike"
+      ],
+      "properties": [
+        {
+          "name": "type",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "topic",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "TaskPriorized",
+      "extends": [
+        "bpmn:Process",
+        "camunda:ExternalCapable"
+      ],
+      "properties": [
+        {
+          "name": "taskPriority",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "Properties",
+      "superClass": [
+        "Element"
+      ],
+      "meta": {
+        "allowedIn": [ "*" ]
+      },
+      "properties": [
+        {
+          "name": "values",
+          "type": "Property",
+          "isMany": true
+        }
+      ]
+    },
+    {
+      "name": "Property",
+      "superClass": [
+        "Element"
+      ],
+      "properties": [
+        {
+          "name": "id",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "name",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "value",
+          "type": "String",
+          "isAttr": true
+        }
+      ]
+    },
+    {
+      "name": "Connector",
+      "superClass": [
+        "Element"
+      ],
+      "meta": {
+        "allowedIn": [
+          "camunda:ServiceTaskLike"
+        ]
+      },
+      "properties": [
+        {
+          "name": "inputOutput",
+          "type": "InputOutput"
+        },
+        {
+          "name": "connectorId",
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "InputOutput",
+      "superClass": [
+        "Element"
+      ],
+      "meta": {
+        "allowedIn": [
+          "bpmn:FlowNode",
+          "camunda:Connector"
+        ]
+      },
+      "properties": [
+        {
+          "name": "inputOutput",
+          "type": "InputOutput"
+        },
+        {
+          "name": "connectorId",
+          "type": "String"
+        },
+        {
+          "name": "inputParameters",
+          "isMany": true,
+          "type": "InputParameter"
+        },
+        {
+          "name": "outputParameters",
+          "isMany": true,
+          "type": "OutputParameter"
+        }
+      ]
+    },
+    {
+      "name": "InputOutputParameter",
+      "properties": [
+        {
+          "name": "name",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "value",
+          "isBody": true,
+          "type": "String"
+        },
+        {
+          "name": "definition",
+          "type": "InputOutputParameterDefinition"
+        }
+      ]
+    },
+    {
+      "name": "InputOutputParameterDefinition",
+      "isAbstract": true
+    },
+    {
+      "name": "List",
+      "superClass": [ "InputOutputParameterDefinition" ],
+      "properties": [
+        {
+          "name": "items",
+          "isMany": true,
+          "type": "InputOutputParameterDefinition"
+        }
+      ]
+    },
+    {
+      "name": "Map",
+      "superClass": [ "InputOutputParameterDefinition" ],
+      "properties": [
+        {
+          "name": "entries",
+          "isMany": true,
+          "type": "Entry"
+        }
+      ]
+    },
+    {
+      "name": "Entry",
+      "properties": [
+        {
+          "name": "key",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "value",
+          "isBody": true,
+          "type": "String"
+        },
+        {
+          "name": "definition",
+          "type": "InputOutputParameterDefinition"
+        }
+      ]
+    },
+    {
+      "name": "Value",
+      "superClass": [
+        "InputOutputParameterDefinition"
+      ],
+      "properties": [
+        {
+          "name": "id",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "name",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "value",
+          "isBody": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "Script",
+      "superClass": [ "InputOutputParameterDefinition" ],
+      "properties": [
+        {
+          "name": "scriptFormat",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "resource",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "value",
+          "isBody": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "Field",
+      "superClass": [ "Element" ],
+      "meta": {
+        "allowedIn": [
+          "camunda:ServiceTaskLike",
+          "camunda:ExecutionListener",
+          "camunda:TaskListener"
+        ]
+      },
+      "properties": [
+        {
+          "name": "name",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "expression",
+          "type": "String"
+        },
+        {
+          "name": "stringValue",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "string",
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "InputParameter",
+      "superClass": [ "InputOutputParameter" ]
+    },
+    {
+      "name": "OutputParameter",
+      "superClass": [ "InputOutputParameter" ]
+    },
+    {
+      "name": "Collectable",
+      "isAbstract": true,
+      "extends": [ "bpmn:MultiInstanceLoopCharacteristics" ],
+      "superClass": [ "camunda:AsyncCapable" ],
+      "properties": [
+        {
+          "name": "collection",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "elementVariable",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "FailedJobRetryTimeCycle",
+      "superClass": [ "Element" ],
+      "meta": {
+        "allowedIn": [
+          "camunda:AsyncCapable",
+          "bpmn:MultiInstanceLoopCharacteristics"
+        ]
+      },
+      "properties": [
+        {
+          "name": "body",
+          "isBody": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "ExecutionListener",
+      "superClass": [ "Element" ],
+      "meta": {
+        "allowedIn": [
+          "bpmn:Task",
+          "bpmn:ServiceTask",
+          "bpmn:UserTask",
+          "bpmn:BusinessRuleTask",
+          "bpmn:ScriptTask",
+          "bpmn:ReceiveTask",
+          "bpmn:ManualTask",
+          "bpmn:ExclusiveGateway",
+          "bpmn:SequenceFlow",
+          "bpmn:ParallelGateway",
+          "bpmn:InclusiveGateway",
+          "bpmn:EventBasedGateway",
+          "bpmn:StartEvent",
+          "bpmn:IntermediateCatchEvent",
+          "bpmn:IntermediateThrowEvent",
+          "bpmn:EndEvent",
+          "bpmn:BoundaryEvent",
+          "bpmn:CallActivity",
+          "bpmn:SubProcess",
+          "bpmn:Process"
+        ]
+      },
+      "properties": [
+        {
+          "name": "expression",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "class",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "delegateExpression",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "event",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "script",
+          "type": "Script"
+        },
+        {
+          "name": "fields",
+          "type": "Field",
+          "isMany": true
+        }
+      ]
+    },
+    {
+      "name": "TaskListener",
+      "superClass": [ "Element" ],
+      "meta": {
+        "allowedIn": [
+          "bpmn:UserTask"
+        ]
+      },
+      "properties": [
+        {
+          "name": "expression",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "class",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "delegateExpression",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "event",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "script",
+          "type": "Script"
+        },
+        {
+          "name": "fields",
+          "type": "Field",
+          "isMany": true
+        },
+        {
+          "name": "id",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "eventDefinitions",
+          "type": "bpmn:TimerEventDefinition",
+          "isMany": true
+        }
+      ]
+    },
+    {
+      "name": "FormProperty",
+      "superClass": [ "Element" ],
+      "meta": {
+        "allowedIn": [
+          "bpmn:StartEvent",
+          "bpmn:UserTask"
+        ]
+      },
+      "properties": [
+        {
+          "name": "id",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "name",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "type",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "required",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "readable",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "writable",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "variable",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "expression",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "datePattern",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "default",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "values",
+          "type": "Value",
+          "isMany": true
+        }
+      ]
+    },
+    {
+      "name": "FormData",
+      "superClass": [ "Element" ],
+      "meta": {
+        "allowedIn": [
+          "bpmn:StartEvent",
+          "bpmn:UserTask"
+        ]
+      },
+      "properties": [
+        {
+          "name": "fields",
+          "type": "FormField",
+          "isMany": true
+        },
+        {
+          "name": "businessKey",
+          "type": "String",
+          "isAttr": true
+        }
+      ]
+    },
+    {
+      "name": "FormField",
+      "superClass": [ "Element" ],
+      "properties": [
+        {
+          "name": "id",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "label",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "type",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "datePattern",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "defaultValue",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "properties",
+          "type": "Properties"
+        },
+        {
+          "name": "validation",
+          "type": "Validation"
+        },
+        {
+          "name": "values",
+          "type": "Value",
+          "isMany": true
+        }
+      ]
+    },
+    {
+      "name": "Validation",
+      "superClass": [ "Element" ],
+      "properties": [
+        {
+          "name": "constraints",
+          "type": "Constraint",
+          "isMany": true
+        }
+      ]
+    },
+    {
+      "name": "Constraint",
+      "superClass": [ "Element" ],
+      "properties": [
+        {
+          "name": "name",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "config",
+          "type": "String",
+          "isAttr": true
+        }
+      ]
+    },
+    {
+      "name": "ConditionalEventDefinition",
+      "isAbstract": true,
+      "extends": [
+        "bpmn:ConditionalEventDefinition"
+      ],
+      "properties": [
+        {
+          "name": "variableName",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "variableEvents",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    }
+  ],
+  "emumerations": [ ]
+}
diff --git a/ruoyi-ui/apps/web-antd/src/package/designer/plugins/descriptor/flowableDescriptor.json b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/descriptor/flowableDescriptor.json
new file mode 100644
index 0000000..7b66155
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/descriptor/flowableDescriptor.json
@@ -0,0 +1,1230 @@
+{
+  "name": "Flowable",
+  "uri": "http://flowable.org/bpmn",
+  "prefix": "flowable",
+  "xml": {
+    "tagAlias": "lowerCase"
+  },
+  "associations": [],
+  "types": [
+    {
+      "name": "InOutBinding",
+      "superClass": ["Element"],
+      "isAbstract": true,
+      "properties": [
+        {
+          "name": "source",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "sourceExpression",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "target",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "businessKey",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "local",
+          "isAttr": true,
+          "type": "Boolean",
+          "default": false
+        },
+        {
+          "name": "variables",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "In",
+      "superClass": ["InOutBinding"],
+      "meta": {
+        "allowedIn": ["bpmn:CallActivity"]
+      }
+    },
+    {
+      "name": "Out",
+      "superClass": ["InOutBinding"],
+      "meta": {
+        "allowedIn": ["bpmn:CallActivity"]
+      }
+    },
+    {
+      "name": "AsyncCapable",
+      "isAbstract": true,
+      "extends": ["bpmn:Activity", "bpmn:Gateway", "bpmn:Event"],
+      "properties": [
+        {
+          "name": "async",
+          "isAttr": true,
+          "type": "Boolean",
+          "default": false
+        },
+        {
+          "name": "asyncBefore",
+          "isAttr": true,
+          "type": "Boolean",
+          "default": false
+        },
+        {
+          "name": "asyncAfter",
+          "isAttr": true,
+          "type": "Boolean",
+          "default": false
+        },
+        {
+          "name": "exclusive",
+          "isAttr": true,
+          "type": "Boolean",
+          "default": true
+        }
+      ]
+    },
+    {
+      "name": "JobPriorized",
+      "isAbstract": true,
+      "extends": ["bpmn:Process", "flowable:AsyncCapable"],
+      "properties": [
+        {
+          "name": "jobPriority",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "SignalEventDefinition",
+      "isAbstract": true,
+      "extends": ["bpmn:SignalEventDefinition"],
+      "properties": [
+        {
+          "name": "async",
+          "isAttr": true,
+          "type": "Boolean",
+          "default": false
+        }
+      ]
+    },
+    {
+      "name": "ErrorEventDefinition",
+      "isAbstract": true,
+      "extends": ["bpmn:ErrorEventDefinition"],
+      "properties": [
+        {
+          "name": "errorCodeVariable",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "errorMessageVariable",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "Error",
+      "isAbstract": true,
+      "extends": ["bpmn:Error"],
+      "properties": [
+        {
+          "name": "flowable:errorMessage",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "PotentialStarter",
+      "superClass": ["Element"],
+      "properties": [
+        {
+          "name": "resourceAssignmentExpression",
+          "type": "bpmn:ResourceAssignmentExpression"
+        }
+      ]
+    },
+    {
+      "name": "FormSupported",
+      "isAbstract": true,
+      "extends": ["bpmn:StartEvent", "bpmn:UserTask"],
+      "properties": [
+        {
+          "name": "formHandlerClass",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "formKey",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "formType",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "formReadOnly",
+          "isAttr": true,
+          "type": "Boolean",
+          "default": false
+        },
+        {
+          "name": "formInit",
+          "isAttr": true,
+          "type": "Boolean",
+          "default": true
+        }
+      ]
+    },
+    {
+      "name": "TemplateSupported",
+      "isAbstract": true,
+      "extends": ["bpmn:Process", "bpmn:FlowElement"],
+      "properties": [
+        {
+          "name": "modelerTemplate",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "Initiator",
+      "isAbstract": true,
+      "extends": ["bpmn:StartEvent"],
+      "properties": [
+        {
+          "name": "initiator",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "ScriptTask",
+      "isAbstract": true,
+      "extends": ["bpmn:ScriptTask"],
+      "properties": [
+        {
+          "name": "resultVariable",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "resource",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "Process",
+      "isAbstract": true,
+      "extends": ["bpmn:Process"],
+      "properties": [
+        {
+          "name": "candidateStarterGroups",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "candidateStarterUsers",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "versionTag",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "historyTimeToLive",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "isStartableInTasklist",
+          "isAttr": true,
+          "type": "Boolean",
+          "default": true
+        }
+      ]
+    },
+    {
+      "name": "EscalationEventDefinition",
+      "isAbstract": true,
+      "extends": ["bpmn:EscalationEventDefinition"],
+      "properties": [
+        {
+          "name": "escalationCodeVariable",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "FormalExpression",
+      "isAbstract": true,
+      "extends": ["bpmn:FormalExpression"],
+      "properties": [
+        {
+          "name": "resource",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "Assignable",
+      "extends": ["bpmn:UserTask"],
+      "properties": [
+        {
+          "name": "assignee",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "candidateUsers",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "candidateGroups",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "dueDate",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "followUpDate",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "priority",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "dataType",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "text",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "deptId",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "Assignee",
+      "supperClass": "Element",
+      "meta": {
+        "allowedIn": ["*"]
+      },
+      "properties": [
+        {
+          "name": "label",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "viewId",
+          "type": "Number",
+          "isAttr": true
+        }
+      ]
+    },
+    {
+      "name": "CallActivity",
+      "extends": ["bpmn:CallActivity"],
+      "properties": [
+        {
+          "name": "calledElementBinding",
+          "isAttr": true,
+          "type": "String",
+          "default": "latest"
+        },
+        {
+          "name": "calledElementVersion",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "calledElementVersionTag",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "calledElementTenantId",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "caseRef",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "caseBinding",
+          "isAttr": true,
+          "type": "String",
+          "default": "latest"
+        },
+        {
+          "name": "caseVersion",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "caseTenantId",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "variableMappingClass",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "variableMappingDelegateExpression",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "ServiceTaskLike",
+      "extends": [
+        "bpmn:ServiceTask",
+        "bpmn:BusinessRuleTask",
+        "bpmn:SendTask",
+        "bpmn:MessageEventDefinition"
+      ],
+      "properties": [
+        {
+          "name": "expression",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "class",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "delegateExpression",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "resultVariable",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "DmnCapable",
+      "extends": ["bpmn:BusinessRuleTask"],
+      "properties": [
+        {
+          "name": "decisionRef",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "decisionRefBinding",
+          "isAttr": true,
+          "type": "String",
+          "default": "latest"
+        },
+        {
+          "name": "decisionRefVersion",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "mapDecisionResult",
+          "isAttr": true,
+          "type": "String",
+          "default": "resultList"
+        },
+        {
+          "name": "decisionRefTenantId",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "ExternalCapable",
+      "extends": ["flowable:ServiceTaskLike"],
+      "properties": [
+        {
+          "name": "type",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "topic",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "TaskPriorized",
+      "extends": ["bpmn:Process", "flowable:ExternalCapable"],
+      "properties": [
+        {
+          "name": "taskPriority",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "Properties",
+      "superClass": ["Element"],
+      "meta": {
+        "allowedIn": ["*"]
+      },
+      "properties": [
+        {
+          "name": "values",
+          "type": "Property",
+          "isMany": true
+        }
+      ]
+    },
+    {
+      "name": "Property",
+      "superClass": ["Element"],
+      "properties": [
+        {
+          "name": "id",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "name",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "value",
+          "type": "String",
+          "isAttr": true
+        }
+      ]
+    },
+    {
+      "name": "Button",
+      "superClass": ["Element"],
+      "meta": {
+        "allowedIn": ["bpmn:UserTask"]
+      },
+      "properties": [
+        {
+          "name": "id",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "name",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "code",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "isHide",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "next",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "sort",
+          "type": "Integer",
+          "isAttr": true
+        }
+      ]
+    },
+    {
+      "name": "Assignee",
+      "superClass": ["Element"],
+      "meta": {
+        "allowedIn": ["bpmn:UserTask"]
+      },
+      "properties": [
+        {
+          "name": "id",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "type",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "value",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "condition",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "operationType",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "sort",
+          "type": "Integer",
+          "isAttr": true
+        }
+      ]
+    },
+    {
+      "name": "Connector",
+      "superClass": ["Element"],
+      "meta": {
+        "allowedIn": ["flowable:ServiceTaskLike"]
+      },
+      "properties": [
+        {
+          "name": "inputOutput",
+          "type": "InputOutput"
+        },
+        {
+          "name": "connectorId",
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "InputOutput",
+      "superClass": ["Element"],
+      "meta": {
+        "allowedIn": ["bpmn:FlowNode", "flowable:Connector"]
+      },
+      "properties": [
+        {
+          "name": "inputOutput",
+          "type": "InputOutput"
+        },
+        {
+          "name": "connectorId",
+          "type": "String"
+        },
+        {
+          "name": "inputParameters",
+          "isMany": true,
+          "type": "InputParameter"
+        },
+        {
+          "name": "outputParameters",
+          "isMany": true,
+          "type": "OutputParameter"
+        }
+      ]
+    },
+    {
+      "name": "InputOutputParameter",
+      "properties": [
+        {
+          "name": "name",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "value",
+          "isBody": true,
+          "type": "String"
+        },
+        {
+          "name": "definition",
+          "type": "InputOutputParameterDefinition"
+        }
+      ]
+    },
+    {
+      "name": "InputOutputParameterDefinition",
+      "isAbstract": true
+    },
+    {
+      "name": "List",
+      "superClass": ["InputOutputParameterDefinition"],
+      "properties": [
+        {
+          "name": "items",
+          "isMany": true,
+          "type": "InputOutputParameterDefinition"
+        }
+      ]
+    },
+    {
+      "name": "Map",
+      "superClass": ["InputOutputParameterDefinition"],
+      "properties": [
+        {
+          "name": "entries",
+          "isMany": true,
+          "type": "Entry"
+        }
+      ]
+    },
+    {
+      "name": "Entry",
+      "properties": [
+        {
+          "name": "key",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "value",
+          "isBody": true,
+          "type": "String"
+        },
+        {
+          "name": "definition",
+          "type": "InputOutputParameterDefinition"
+        }
+      ]
+    },
+    {
+      "name": "Value",
+      "superClass": ["InputOutputParameterDefinition"],
+      "properties": [
+        {
+          "name": "id",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "name",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "value",
+          "isBody": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "Script",
+      "superClass": ["InputOutputParameterDefinition"],
+      "properties": [
+        {
+          "name": "scriptFormat",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "resource",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "value",
+          "isBody": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "Field",
+      "superClass": ["Element"],
+      "meta": {
+        "allowedIn": [
+          "flowable:ServiceTaskLike",
+          "flowable:ExecutionListener",
+          "flowable:TaskListener",
+          "bpmn:ServiceTask"
+        ]
+      },
+      "properties": [
+        {
+          "name": "name",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "expression",
+          "type": "String"
+        },
+        {
+          "name": "stringValue",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "string",
+          "type": "String"
+        },
+        {
+          "name": "htmlVar",
+          "type": "Expression"
+        }
+      ]
+    },
+    {
+      "name": "ChildField",
+      "superClass": ["Element"],
+      "properties": [
+        {
+          "name": "id",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "name",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "type",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "required",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "readable",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "writable",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "variable",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "expression",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "datePattern",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "default",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "values",
+          "type": "Value",
+          "isMany": true
+        }
+      ]
+    },
+    {
+      "name": "InputParameter",
+      "superClass": ["InputOutputParameter"]
+    },
+    {
+      "name": "OutputParameter",
+      "superClass": ["InputOutputParameter"]
+    },
+    {
+      "name": "Collectable",
+      "isAbstract": true,
+      "extends": ["bpmn:MultiInstanceLoopCharacteristics"],
+      "superClass": ["flowable:AsyncCapable"],
+      "properties": [
+        {
+          "name": "collection",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "elementVariable",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "FailedJobRetryTimeCycle",
+      "superClass": ["Element"],
+      "meta": {
+        "allowedIn": [
+          "flowable:AsyncCapable",
+          "bpmn:MultiInstanceLoopCharacteristics"
+        ]
+      },
+      "properties": [
+        {
+          "name": "body",
+          "isBody": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "ExecutionListener",
+      "superClass": ["Element"],
+      "meta": {
+        "allowedIn": [
+          "bpmn:Task",
+          "bpmn:ServiceTask",
+          "bpmn:UserTask",
+          "bpmn:BusinessRuleTask",
+          "bpmn:ScriptTask",
+          "bpmn:ReceiveTask",
+          "bpmn:ManualTask",
+          "bpmn:ExclusiveGateway",
+          "bpmn:SequenceFlow",
+          "bpmn:ParallelGateway",
+          "bpmn:InclusiveGateway",
+          "bpmn:EventBasedGateway",
+          "bpmn:StartEvent",
+          "bpmn:IntermediateCatchEvent",
+          "bpmn:IntermediateThrowEvent",
+          "bpmn:EndEvent",
+          "bpmn:BoundaryEvent",
+          "bpmn:CallActivity",
+          "bpmn:SubProcess",
+          "bpmn:Process"
+        ]
+      },
+      "properties": [
+        {
+          "name": "expression",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "class",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "delegateExpression",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "event",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "script",
+          "type": "Script"
+        },
+        {
+          "name": "fields",
+          "type": "Field",
+          "isMany": true
+        }
+      ]
+    },
+    {
+      "name": "TaskListener",
+      "superClass": ["Element"],
+      "meta": {
+        "allowedIn": ["bpmn:UserTask"]
+      },
+      "properties": [
+        {
+          "name": "expression",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "class",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "delegateExpression",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "event",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "script",
+          "type": "Script"
+        },
+        {
+          "name": "fields",
+          "type": "Field",
+          "isMany": true
+        }
+      ]
+    },
+    {
+      "name": "FormProperty",
+      "superClass": ["Element"],
+      "meta": {
+        "allowedIn": ["bpmn:StartEvent", "bpmn:UserTask"]
+      },
+      "properties": [
+        {
+          "name": "id",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "name",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "type",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "required",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "readable",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "writable",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "variable",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "expression",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "datePattern",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "default",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "values",
+          "type": "Value",
+          "isMany": true
+        },
+        {
+          "name": "children",
+          "type": "ChildField",
+          "isMany": true
+        },
+        {
+          "name": "extensionElements",
+          "type": "bpmn:ExtensionElements",
+          "isMany": true
+        }
+      ]
+    },
+    {
+      "name": "FormData",
+      "superClass": ["Element"],
+      "meta": {
+        "allowedIn": ["bpmn:StartEvent", "bpmn:UserTask"]
+      },
+      "properties": [
+        {
+          "name": "fields",
+          "type": "FormField",
+          "isMany": true
+        },
+        {
+          "name": "businessKey",
+          "type": "String",
+          "isAttr": true
+        }
+      ]
+    },
+    {
+      "name": "FormField",
+      "superClass": ["Element"],
+      "properties": [
+        {
+          "name": "id",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "label",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "type",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "datePattern",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "defaultValue",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "properties",
+          "type": "Properties"
+        },
+        {
+          "name": "validation",
+          "type": "Validation"
+        },
+        {
+          "name": "values",
+          "type": "Value",
+          "isMany": true
+        }
+      ]
+    },
+    {
+      "name": "Validation",
+      "superClass": ["Element"],
+      "properties": [
+        {
+          "name": "constraints",
+          "type": "Constraint",
+          "isMany": true
+        }
+      ]
+    },
+    {
+      "name": "Constraint",
+      "superClass": ["Element"],
+      "properties": [
+        {
+          "name": "name",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "config",
+          "type": "String",
+          "isAttr": true
+        }
+      ]
+    },
+    {
+      "name": "ConditionalEventDefinition",
+      "isAbstract": true,
+      "extends": ["bpmn:ConditionalEventDefinition"],
+      "properties": [
+        {
+          "name": "variableName",
+          "isAttr": true,
+          "type": "String"
+        },
+        {
+          "name": "variableEvent",
+          "isAttr": true,
+          "type": "String"
+        }
+      ]
+    },
+    {
+      "name": "Condition",
+      "superClass": ["Element"],
+      "meta": {
+        "allowedIn": ["bpmn:SequenceFlow"]
+      },
+      "properties": [
+        {
+          "name": "id",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "field",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "compare",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "value",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "logic",
+          "type": "String",
+          "isAttr": true
+        },
+        {
+          "name": "sort",
+          "type": "Integer",
+          "isAttr": true
+        }
+      ]
+    }
+  ],
+  "emumerations": []
+}
diff --git a/ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/activiti/activitiExtension.js b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/activiti/activitiExtension.js
new file mode 100644
index 0000000..cfa5589
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/activiti/activitiExtension.js
@@ -0,0 +1,73 @@
+'use strict';
+
+import { some } from '#/utils/min-dash.js';
+
+var ALLOWED_TYPES = {
+  FailedJobRetryTimeCycle: ['bpmn:StartEvent', 'bpmn:BoundaryEvent', 'bpmn:IntermediateCatchEvent', 'bpmn:Activity'],
+  Connector: ['bpmn:EndEvent', 'bpmn:IntermediateThrowEvent'],
+  Field: ['bpmn:EndEvent', 'bpmn:IntermediateThrowEvent']
+};
+
+function is(element, type) {
+  return element && typeof element.$instanceOf === 'function' && element.$instanceOf(type);
+}
+
+function exists(element) {
+  return element && element.length;
+}
+
+function includesType(collection, type) {
+  return (
+    exists(collection) &&
+    some(collection, function(element) {
+      return is(element, type);
+    })
+  );
+}
+
+function anyType(element, types) {
+  return some(types, function(type) {
+    return is(element, type);
+  });
+}
+
+function isAllowed(propName, propDescriptor, newElement) {
+  var name = propDescriptor.name;
+  var types = ALLOWED_TYPES[name.replace(/activiti:/, '')];
+
+  return name === propName && anyType(newElement, types);
+}
+
+export default function ActivitiModdleExtension(eventBus) {
+  eventBus.on(
+    'property.clone',
+    function(context) {
+      var newElement = context.newElement;
+      var propDescriptor = context.propertyDescriptor;
+
+      this.canCloneProperty(newElement, propDescriptor);
+    },
+    this
+  );
+}
+
+ActivitiModdleExtension.$inject = ['eventBus'];
+
+ActivitiModdleExtension.prototype.canCloneProperty = function(newElement, propDescriptor) {
+  if (isAllowed('activiti:FailedJobRetryTimeCycle', propDescriptor, newElement)) {
+    return (
+      includesType(newElement.eventDefinitions, 'bpmn:TimerEventDefinition') ||
+      includesType(newElement.eventDefinitions, 'bpmn:SignalEventDefinition') ||
+      is(newElement.loopCharacteristics, 'bpmn:MultiInstanceLoopCharacteristics')
+    );
+  }
+
+  if (isAllowed('activiti:Connector', propDescriptor, newElement)) {
+    return includesType(newElement.eventDefinitions, 'bpmn:MessageEventDefinition');
+  }
+
+  if (isAllowed('activiti:Field', propDescriptor, newElement)) {
+    return includesType(newElement.eventDefinitions, 'bpmn:MessageEventDefinition');
+  }
+};
+
diff --git a/ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/activiti/index.js b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/activiti/index.js
new file mode 100644
index 0000000..9132e5a
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/activiti/index.js
@@ -0,0 +1,10 @@
+/*
+ * @author igdianov
+ * address https://github.com/igdianov/activiti-bpmn-moddle
+ * */
+
+import ActivitiModdleExtension from './activitiExtension.js'
+export default {
+  __init__: ['ActivitiModdleExtension'],
+  ActivitiModdleExtension: ['type', ActivitiModdleExtension]
+};
diff --git a/ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/camunda/extension.js b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/camunda/extension.js
new file mode 100644
index 0000000..307766e
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/camunda/extension.js
@@ -0,0 +1,144 @@
+'use strict';
+
+import { some, isObject, isFunction } from '#/utils/min-dash.js';
+
+var WILDCARD = '*';
+
+export default function CamundaModdleExtension(eventBus) {
+  var self = this;
+
+  eventBus.on('moddleCopy.canCopyProperty', function(context) {
+    var property = context.property;
+    var parent = context.parent;
+
+    return self.canCopyProperty(property, parent);
+  });
+}
+
+CamundaModdleExtension.$inject = ['eventBus'];
+
+/**
+ * Check wether to disallow copying property.
+ */
+CamundaModdleExtension.prototype.canCopyProperty = function(property, parent) {
+  // (1) check wether property is allowed in parent
+  if (isObject(property) && !isAllowedInParent(property, parent)) {
+    return false;
+  }
+
+  // (2) check more complex scenarios
+
+  if (is(property, 'camunda:InputOutput') && !this.canHostInputOutput(parent)) {
+    return false;
+  }
+
+  if (isAny(property, ['camunda:Connector', 'camunda:Field']) && !this.canHostConnector(parent)) {
+    return false;
+  }
+
+  if (is(property, 'camunda:In') && !this.canHostIn(parent)) {
+    return false;
+  }
+};
+
+CamundaModdleExtension.prototype.canHostInputOutput = function(parent) {
+  // allowed in camunda:Connector
+  var connector = getParent(parent, 'camunda:Connector');
+
+  if (connector) {
+    return true;
+  }
+
+  // special rules inside bpmn:FlowNode
+  var flowNode = getParent(parent, 'bpmn:FlowNode');
+
+  if (!flowNode) {
+    return false;
+  }
+
+  if (isAny(flowNode, ['bpmn:StartEvent', 'bpmn:Gateway', 'bpmn:BoundaryEvent'])) {
+    return false;
+  }
+
+  if (is(flowNode, 'bpmn:SubProcess') && flowNode.get('triggeredByEvent')) {
+    return false;
+  }
+
+  return true;
+};
+
+CamundaModdleExtension.prototype.canHostConnector = function(parent) {
+  var serviceTaskLike = getParent(parent, 'camunda:ServiceTaskLike');
+
+  if (is(serviceTaskLike, 'bpmn:MessageEventDefinition')) {
+    // only allow on throw and end events
+    return getParent(parent, 'bpmn:IntermediateThrowEvent') || getParent(parent, 'bpmn:EndEvent');
+  }
+
+  return true;
+};
+
+CamundaModdleExtension.prototype.canHostIn = function(parent) {
+  var callActivity = getParent(parent, 'bpmn:CallActivity');
+
+  if (callActivity) {
+    return true;
+  }
+
+  var signalEventDefinition = getParent(parent, 'bpmn:SignalEventDefinition');
+
+  if (signalEventDefinition) {
+    // only allow on throw and end events
+    return getParent(parent, 'bpmn:IntermediateThrowEvent') || getParent(parent, 'bpmn:EndEvent');
+  }
+
+  return true;
+};
+
+// helpers //////////
+
+function is(element, type) {
+  return element && isFunction(element.$instanceOf) && element.$instanceOf(type);
+}
+
+function isAny(element, types) {
+  return some(types, function(t) {
+    return is(element, t);
+  });
+}
+
+function getParent(element, type) {
+  if (!type) {
+    return element.$parent;
+  }
+
+  if (is(element, type)) {
+    return element;
+  }
+
+  if (!element.$parent) {
+    return;
+  }
+
+  return getParent(element.$parent, type);
+}
+
+function isAllowedInParent(property, parent) {
+  // (1) find property descriptor
+  var descriptor = property.$type && property.$model.getTypeDescriptor(property.$type);
+
+  var allowedIn = descriptor && descriptor.meta && descriptor.meta.allowedIn;
+
+  if (!allowedIn || isWildcard(allowedIn)) {
+    return true;
+  }
+
+  // (2) check wether property has parent of allowed type
+  return some(allowedIn, function(type) {
+    return getParent(parent, type);
+  });
+}
+
+function isWildcard(allowedIn) {
+  return allowedIn.indexOf(WILDCARD) !== -1;
+}
diff --git a/ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/camunda/index.js b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/camunda/index.js
new file mode 100644
index 0000000..007c1f4
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/camunda/index.js
@@ -0,0 +1,7 @@
+'use strict';
+
+import CamundaModdleExtension from './extension.js';
+export default {
+  __init__: ['CamundaModdleExtension'],
+  CamundaModdleExtension: ['type', CamundaModdleExtension]
+};
diff --git a/ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/flowable/flowableExtension.js b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/flowable/flowableExtension.js
new file mode 100644
index 0000000..d214d9a
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/flowable/flowableExtension.js
@@ -0,0 +1,72 @@
+'use strict';
+
+import { some } from '#/utils/min-dash.js';
+
+var ALLOWED_TYPES = {
+  FailedJobRetryTimeCycle: ['bpmn:StartEvent', 'bpmn:BoundaryEvent', 'bpmn:IntermediateCatchEvent', 'bpmn:Activity'],
+  Connector: ['bpmn:EndEvent', 'bpmn:IntermediateThrowEvent'],
+  Field: ['bpmn:EndEvent', 'bpmn:IntermediateThrowEvent']
+};
+
+function is(element, type) {
+  return element && typeof element.$instanceOf === 'function' && element.$instanceOf(type);
+}
+
+function exists(element) {
+  return element && element.length;
+}
+
+function includesType(collection, type) {
+  return (
+    exists(collection) &&
+    some(collection, function(element) {
+      return is(element, type);
+    })
+  );
+}
+
+function anyType(element, types) {
+  return some(types, function(type) {
+    return is(element, type);
+  });
+}
+
+function isAllowed(propName, propDescriptor, newElement) {
+  var name = propDescriptor.name;
+  var types = ALLOWED_TYPES[name.replace(/flowable:/, '')];
+
+  return name === propName && anyType(newElement, types);
+}
+
+export default function FlowableModdleExtension(eventBus) {
+  eventBus.on(
+    'property.clone',
+    function(context) {
+      var newElement = context.newElement;
+      var propDescriptor = context.propertyDescriptor;
+
+      this.canCloneProperty(newElement, propDescriptor);
+    },
+    this
+  );
+}
+
+FlowableModdleExtension.$inject = ['eventBus'];
+
+FlowableModdleExtension.prototype.canCloneProperty = function(newElement, propDescriptor) {
+  if (isAllowed('flowable:FailedJobRetryTimeCycle', propDescriptor, newElement)) {
+    return (
+      includesType(newElement.eventDefinitions, 'bpmn:TimerEventDefinition') ||
+      includesType(newElement.eventDefinitions, 'bpmn:SignalEventDefinition') ||
+      is(newElement.loopCharacteristics, 'bpmn:MultiInstanceLoopCharacteristics')
+    );
+  }
+
+  if (isAllowed('flowable:Connector', propDescriptor, newElement)) {
+    return includesType(newElement.eventDefinitions, 'bpmn:MessageEventDefinition');
+  }
+
+  if (isAllowed('flowable:Field', propDescriptor, newElement)) {
+    return includesType(newElement.eventDefinitions, 'bpmn:MessageEventDefinition');
+  }
+};
diff --git a/ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/flowable/index.js b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/flowable/index.js
new file mode 100644
index 0000000..c0c3968
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/flowable/index.js
@@ -0,0 +1,10 @@
+/*
+ * @author igdianov
+ * address https://github.com/igdianov/activiti-bpmn-moddle
+ * */
+
+import FlowableModdleExtension from './flowableExtension.js'
+export default {
+  __init__: ['FlowableModdleExtension'],
+  FlowableModdleExtension: ['type', FlowableModdleExtension]
+};
diff --git a/ruoyi-ui/apps/web-antd/src/package/designer/plugins/palette/CustomPalette.js b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/palette/CustomPalette.js
new file mode 100644
index 0000000..6617bb5
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/palette/CustomPalette.js
@@ -0,0 +1,156 @@
+import PaletteProvider from 'bpmn-js/lib/features/palette/PaletteProvider';
+import { assign } from '#/utils/min-dash.js';
+
+export default function CustomPalette(palette, create, elementFactory, spaceTool, lassoTool, handTool, globalConnect, translate) {
+  PaletteProvider.call(this, palette, create, elementFactory, spaceTool, lassoTool, handTool, globalConnect, translate, 2000);
+}
+
+const F = function() {}; // 鏍稿績锛屽埄鐢ㄧ┖瀵硅薄浣滀负涓粙锛�
+F.prototype = PaletteProvider.prototype; // 鏍稿績锛屽皢鐖剁被鐨勫師鍨嬭祴鍊肩粰绌哄璞锛�
+
+// 鍒╃敤涓粙鍑芥暟閲嶅啓鍘熷瀷閾炬柟娉�
+F.prototype.getPaletteEntries = function() {
+  var actions = {};
+  var create = this._create;
+  var elementFactory = this._elementFactory;
+  var spaceTool = this._spaceTool;
+  var lassoTool = this._lassoTool;
+  var handTool = this._handTool;
+  var globalConnect = this._globalConnect;
+  var translate = this._translate;
+
+  function createAction(type, group, className, title, options) {
+    function createListener(event) {
+      var shape = elementFactory.createShape(assign({ type: type }, options));
+
+      if (options) {
+        shape.businessObject.di.isExpanded = options.isExpanded;
+      }
+
+      create.start(event, shape);
+    }
+
+    var shortType = type.replace(/^bpmn:/, '');
+
+    return {
+      group: group,
+      className: className,
+      title: title || translate('Create {type}', { type: shortType }),
+      action: {
+        dragstart: createListener,
+        click: createListener
+      }
+    };
+  }
+
+  function createSubprocess(event) {
+    var subProcess = elementFactory.createShape({
+      type: 'bpmn:SubProcess',
+      x: 0,
+      y: 0,
+      isExpanded: true
+    });
+
+    var startEvent = elementFactory.createShape({
+      type: 'bpmn:StartEvent',
+      x: 40,
+      y: 82,
+      parent: subProcess
+    });
+
+    create.start(event, [subProcess, startEvent], {
+      hints: {
+        autoSelect: [startEvent]
+      }
+    });
+  }
+
+  function createParticipant(event) {
+    create.start(event, elementFactory.createParticipantShape());
+  }
+
+  assign(actions, {
+    'hand-tool': {
+      group: 'tools',
+      className: 'bpmn-icon-hand-tool',
+      title: translate('Activate the hand tool'),
+      action: {
+        click: function(event) {
+          handTool.activateHand(event);
+        }
+      }
+    },
+    'lasso-tool': {
+      group: 'tools',
+      className: 'bpmn-icon-lasso-tool',
+      title: translate('Activate the lasso tool'),
+      action: {
+        click: function(event) {
+          lassoTool.activateSelection(event);
+        }
+      }
+    },
+    'space-tool': {
+      group: 'tools',
+      className: 'bpmn-icon-space-tool',
+      title: translate('Activate the create/remove space tool'),
+      action: {
+        click: function(event) {
+          spaceTool.activateSelection(event);
+        }
+      }
+    },
+    'global-connect-tool': {
+      group: 'tools',
+      className: 'bpmn-icon-connection-multi',
+      title: translate('Activate the global connect tool'),
+      action: {
+        click: function(event) {
+          globalConnect.toggle(event);
+        }
+      }
+    },
+    'tool-separator': {
+      group: 'tools',
+      separator: true
+    },
+    'create.start-event': createAction('bpmn:StartEvent', 'event', 'bpmn-icon-start-event-none', translate('Create StartEvent')),
+    'create.intermediate-event': createAction(
+      'bpmn:IntermediateThrowEvent',
+      'event',
+      'bpmn-icon-intermediate-event-none',
+      translate('Create Intermediate/Boundary Event')
+    ),
+    'create.end-event': createAction('bpmn:EndEvent', 'event', 'bpmn-icon-end-event-none', translate('Create EndEvent')),
+    'create.exclusive-gateway': createAction('bpmn:ExclusiveGateway', 'gateway', 'bpmn-icon-gateway-none', translate('Create Gateway')),
+    'create.user-task': createAction('bpmn:UserTask', 'activity', 'bpmn-icon-user-task', translate('Create User Task')),
+    'create.data-object': createAction('bpmn:DataObjectReference', 'data-object', 'bpmn-icon-data-object', translate('Create DataObjectReference')),
+    'create.data-store': createAction('bpmn:DataStoreReference', 'data-store', 'bpmn-icon-data-store', translate('Create DataStoreReference')),
+    'create.subprocess-expanded': {
+      group: 'activity',
+      className: 'bpmn-icon-subprocess-expanded',
+      title: translate('Create expanded SubProcess'),
+      action: {
+        dragstart: createSubprocess,
+        click: createSubprocess
+      }
+    },
+    'create.participant-expanded': {
+      group: 'collaboration',
+      className: 'bpmn-icon-participant',
+      title: translate('Create Pool/Participant'),
+      action: {
+        dragstart: createParticipant,
+        click: createParticipant
+      }
+    },
+    'create.group': createAction('bpmn:Group', 'artifact', 'bpmn-icon-group', translate('Create Group'))
+  });
+
+  return actions;
+};
+
+CustomPalette.$inject = ['palette', 'create', 'elementFactory', 'spaceTool', 'lassoTool', 'handTool', 'globalConnect', 'translate'];
+
+CustomPalette.prototype = new F(); // 鏍稿績锛屽皢 F鐨勫疄渚嬭祴鍊肩粰瀛愮被锛�
+CustomPalette.prototype.constructor = CustomPalette; // 淇瀛愮被CustomPalette鐨勬瀯閫犲櫒鎸囧悜锛岄槻姝㈠師鍨嬮摼鐨勬贩涔憋紱
diff --git a/ruoyi-ui/apps/web-antd/src/package/designer/plugins/palette/index.js b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/palette/index.js
new file mode 100644
index 0000000..8d5f03f
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/palette/index.js
@@ -0,0 +1,6 @@
+import CustomPalette from './CustomPalette';
+
+export default {
+  __init__: ['customPalette'],
+  customPalette: ['type', CustomPalette]
+};
diff --git a/ruoyi-ui/apps/web-antd/src/package/designer/plugins/palette/paletteProvider.js b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/palette/paletteProvider.js
new file mode 100644
index 0000000..3ac193a
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/palette/paletteProvider.js
@@ -0,0 +1,160 @@
+import { assign } from '#/utils/min-dash.js';
+
+/**
+ * A palette provider for BPMN 2.0 elements.
+ */
+export default function PaletteProvider(palette, create, elementFactory, spaceTool, lassoTool, handTool, globalConnect, translate) {
+  this._palette = palette;
+  this._create = create;
+  this._elementFactory = elementFactory;
+  this._spaceTool = spaceTool;
+  this._lassoTool = lassoTool;
+  this._handTool = handTool;
+  this._globalConnect = globalConnect;
+  this._translate = translate;
+
+  palette.registerProvider(this);
+}
+
+PaletteProvider.$inject = ['palette', 'create', 'elementFactory', 'spaceTool', 'lassoTool', 'handTool', 'globalConnect', 'translate'];
+
+PaletteProvider.prototype.getPaletteEntries = function() {
+  var actions = {};
+  var create = this._create;
+  var elementFactory = this._elementFactory;
+  var spaceTool = this._spaceTool;
+  var lassoTool = this._lassoTool;
+  var handTool = this._handTool;
+  var globalConnect = this._globalConnect;
+  var translate = this._translate;
+
+  function createAction(type, group, className, title, options) {
+    function createListener(event) {
+      var shape = elementFactory.createShape(assign({ type: type }, options));
+
+      if (options) {
+        shape.businessObject.di.isExpanded = options.isExpanded;
+      }
+
+      create.start(event, shape);
+    }
+
+    var shortType = type.replace(/^bpmn:/, '');
+
+    return {
+      group: group,
+      className: className,
+      title: title || translate('Create {type}', { type: shortType }),
+      action: {
+        dragstart: createListener,
+        click: createListener
+      }
+    };
+  }
+
+  function createSubprocess(event) {
+    var subProcess = elementFactory.createShape({
+      type: 'bpmn:SubProcess',
+      x: 0,
+      y: 0,
+      isExpanded: true
+    });
+
+    var startEvent = elementFactory.createShape({
+      type: 'bpmn:StartEvent',
+      x: 40,
+      y: 82,
+      parent: subProcess
+    });
+
+    create.start(event, [subProcess, startEvent], {
+      hints: {
+        autoSelect: [startEvent]
+      }
+    });
+  }
+
+  function createParticipant(event) {
+    create.start(event, elementFactory.createParticipantShape());
+  }
+
+  assign(actions, {
+    'hand-tool': {
+      group: 'tools',
+      className: 'bpmn-icon-hand-tool',
+      title: translate('Activate the hand tool'),
+      action: {
+        click: function(event) {
+          handTool.activateHand(event);
+        }
+      }
+    },
+    'lasso-tool': {
+      group: 'tools',
+      className: 'bpmn-icon-lasso-tool',
+      title: translate('Activate the lasso tool'),
+      action: {
+        click: function(event) {
+          lassoTool.activateSelection(event);
+        }
+      }
+    },
+    'space-tool': {
+      group: 'tools',
+      className: 'bpmn-icon-space-tool',
+      title: translate('Activate the create/remove space tool'),
+      action: {
+        click: function(event) {
+          spaceTool.activateSelection(event);
+        }
+      }
+    },
+    'global-connect-tool': {
+      group: 'tools',
+      className: 'bpmn-icon-connection-multi',
+      title: translate('Activate the global connect tool'),
+      action: {
+        click: function(event) {
+          globalConnect.toggle(event);
+        }
+      }
+    },
+    'tool-separator': {
+      group: 'tools',
+      separator: true
+    },
+    'create.start-event': createAction('bpmn:StartEvent', 'event', 'bpmn-icon-start-event-none', translate('Create StartEvent')),
+    'create.intermediate-event': createAction(
+      'bpmn:IntermediateThrowEvent',
+      'event',
+      'bpmn-icon-intermediate-event-none',
+      translate('Create Intermediate/Boundary Event')
+    ),
+    'create.end-event': createAction('bpmn:EndEvent', 'event', 'bpmn-icon-end-event-none', translate('Create EndEvent')),
+    'create.exclusive-gateway': createAction('bpmn:ExclusiveGateway', 'gateway', 'bpmn-icon-gateway-none', translate('Create Gateway')),
+    'create.user-task': createAction('bpmn:UserTask', 'activity', 'bpmn-icon-user-task', translate('Create User Task')),
+    'create.data-object': createAction('bpmn:DataObjectReference', 'data-object', 'bpmn-icon-data-object', translate('Create DataObjectReference')),
+    'create.data-store': createAction('bpmn:DataStoreReference', 'data-store', 'bpmn-icon-data-store', translate('Create DataStoreReference')),
+    'create.subprocess-expanded': {
+      group: 'activity',
+      className: 'bpmn-icon-subprocess-expanded',
+      title: translate('Create expanded SubProcess'),
+      action: {
+        dragstart: createSubprocess,
+        click: createSubprocess
+      }
+    },
+    'create.participant-expanded': {
+      group: 'collaboration',
+      className: 'bpmn-icon-participant',
+      title: translate('Create Pool/Participant'),
+      action: {
+        dragstart: createParticipant,
+        click: createParticipant
+      }
+    },
+    'create.group': createAction('bpmn:Group', 'artifact', 'bpmn-icon-group', translate('Create Group'))
+  });
+
+  return actions;
+};
diff --git a/ruoyi-ui/apps/web-antd/src/package/designer/plugins/translate/customTranslate.js b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/translate/customTranslate.js
new file mode 100644
index 0000000..12cae81
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/translate/customTranslate.js
@@ -0,0 +1,41 @@
+// import translations from "./zh";
+//
+// export default function customTranslate(template, replacements) {
+//   replacements = replacements || {};
+//
+//   // Translate
+//   template = translations[template] || template;
+//
+//   // Replace
+//   return template.replace(/{([^}]+)}/g, function(_, key) {
+//     let str = replacements[key];
+//     if (
+//       translations[replacements[key]] !== null &&
+//       translations[replacements[key]] !== "undefined"
+//     ) {
+//       // eslint-disable-next-line no-mixed-spaces-and-tabs
+//       str = translations[replacements[key]];
+//       // eslint-disable-next-line no-mixed-spaces-and-tabs
+//     }
+//     return str || "{" + key + "}";
+//   });
+// }
+
+export default function customTranslate(translations) {
+  return function(template, replacements) {
+    replacements = replacements || {};
+    // Translate
+    template = translations[template] || template;
+
+    // Replace
+    return template.replace(/{([^}]+)}/g, function(_, key) {
+      let str = replacements[key];
+      if (translations[replacements[key]] !== null && translations[replacements[key]] !== undefined) {
+        // eslint-disable-next-line no-mixed-spaces-and-tabs
+        str = translations[replacements[key]];
+        // eslint-disable-next-line no-mixed-spaces-and-tabs
+      }
+      return str || '{' + key + '}';
+    });
+  };
+}
diff --git a/ruoyi-ui/apps/web-antd/src/package/designer/plugins/translate/zh.js b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/translate/zh.js
new file mode 100644
index 0000000..03b20ab
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/translate/zh.js
@@ -0,0 +1,238 @@
+/**
+ * This is a sample file that should be replaced with the actual translation.
+ *
+ * Checkout https://github.com/bpmn-io/bpmn-js-i18n for a list of available
+ * translations and labels to translate.
+ */
+export default {
+  // 娣诲姞閮ㄥ垎
+  'Append EndEvent': '杩藉姞缁撴潫浜嬩欢',
+  'Append Gateway': '杩藉姞缃戝叧',
+  'Append Task': '杩藉姞浠诲姟',
+  'Append Intermediate/Boundary Event': '杩藉姞涓棿鎶涘嚭浜嬩欢/杈圭晫浜嬩欢',
+
+  'Activate the global connect tool': '婵�娲诲叏灞�杩炴帴宸ュ叿',
+  'Append {type}': '娣诲姞 {type}',
+  'Add Lane above': '鍦ㄤ笂闈㈡坊鍔犻亾',
+  'Divide into two Lanes': '鍒嗗壊鎴愪袱涓亾',
+  'Divide into three Lanes': '鍒嗗壊鎴愪笁涓亾',
+  'Add Lane below': '鍦ㄤ笅闈㈡坊鍔犻亾',
+  'Append compensation activity': '杩藉姞琛ュ伩娲诲姩',
+  'Change type': '淇敼绫诲瀷',
+  'Connect using Association': '浣跨敤鍏宠仈杩炴帴',
+  'Connect using Sequence/MessageFlow or Association': '浣跨敤椤哄簭/娑堟伅娴佹垨鑰呭叧鑱旇繛鎺�',
+  'Connect using DataInputAssociation': '浣跨敤鏁版嵁杈撳叆鍏宠仈杩炴帴',
+  Remove: '绉婚櫎',
+  'Activate the hand tool': '婵�娲绘姄鎵嬪伐鍏�',
+  'Activate the lasso tool': '婵�娲诲绱㈠伐鍏�',
+  'Activate the create/remove space tool': '婵�娲诲垱寤�/鍒犻櫎绌洪棿宸ュ叿',
+  'Create expanded SubProcess': '鍒涘缓鎵╁睍瀛愯繃绋�',
+  'Create IntermediateThrowEvent/BoundaryEvent': '鍒涘缓涓棿鎶涘嚭浜嬩欢/杈圭晫浜嬩欢',
+  'Create Pool/Participant': '鍒涘缓姹�/鍙備笌鑰�',
+  'Parallel Multi Instance': '骞惰澶氶噸浜嬩欢',
+  'Sequential Multi Instance': '鏃跺簭澶氶噸浜嬩欢',
+  DataObjectReference: '鏁版嵁瀵硅薄鍙傝��',
+  DataStoreReference: '鏁版嵁瀛樺偍鍙傝��',
+  Loop: '寰幆',
+  'Ad-hoc': '鍗冲腑',
+  'Create {type}': '鍒涘缓 {type}',
+  Task: '浠诲姟',
+  'Send Task': '鍙戦�佷换鍔�',
+  'Receive Task': '鎺ユ敹浠诲姟',
+  'User Task': '鐢ㄦ埛浠诲姟',
+  'Manual Task': '鎵嬪伐浠诲姟',
+  'Business Rule Task': '涓氬姟瑙勫垯浠诲姟',
+  'Service Task': '鏈嶅姟浠诲姟',
+  'Script Task': '鑴氭湰浠诲姟',
+  'Call Activity': '璋冪敤娲诲姩',
+  'Sub Process (collapsed)': '瀛愭祦绋嬶紙鎶樺彔鐨勶級',
+  'Sub Process (expanded)': '瀛愭祦绋嬶紙灞曞紑鐨勶級',
+  'Start Event': '寮�濮嬩簨浠�',
+  StartEvent: '寮�濮嬩簨浠�',
+  'Intermediate Throw Event': '涓棿浜嬩欢',
+  'End Event': '缁撴潫浜嬩欢',
+  EndEvent: '缁撴潫浜嬩欢',
+  'Create StartEvent': '鍒涘缓寮�濮嬩簨浠�',
+  'Create EndEvent': '鍒涘缓缁撴潫浜嬩欢',
+  'Create Task': '鍒涘缓浠诲姟',
+  'Create User Task': '鍒涘缓鐢ㄦ埛浠诲姟',
+  'Create Gateway': '鍒涘缓缃戝叧',
+  'Create DataObjectReference': '鍒涘缓鏁版嵁瀵硅薄',
+  'Create DataStoreReference': '鍒涘缓鏁版嵁瀛樺偍',
+  'Create Group': '鍒涘缓鍒嗙粍',
+  'Create Intermediate/Boundary Event': '鍒涘缓涓棿/杈圭晫浜嬩欢',
+  'Message Start Event': '娑堟伅寮�濮嬩簨浠�',
+  'Timer Start Event': '瀹氭椂寮�濮嬩簨浠�',
+  'Conditional Start Event': '鏉′欢寮�濮嬩簨浠�',
+  'Signal Start Event': '淇″彿寮�濮嬩簨浠�',
+  'Error Start Event': '閿欒寮�濮嬩簨浠�',
+  'Escalation Start Event': '鍗囩骇寮�濮嬩簨浠�',
+  'Compensation Start Event': '琛ュ伩寮�濮嬩簨浠�',
+  'Message Start Event (non-interrupting)': '娑堟伅寮�濮嬩簨浠讹紙闈炰腑鏂級',
+  'Timer Start Event (non-interrupting)': '瀹氭椂寮�濮嬩簨浠讹紙闈炰腑鏂級',
+  'Conditional Start Event (non-interrupting)': '鏉′欢寮�濮嬩簨浠讹紙闈炰腑鏂級',
+  'Signal Start Event (non-interrupting)': '淇″彿寮�濮嬩簨浠讹紙闈炰腑鏂級',
+  'Escalation Start Event (non-interrupting)': '鍗囩骇寮�濮嬩簨浠讹紙闈炰腑鏂級',
+  'Message Intermediate Catch Event': '娑堟伅涓棿鎹曡幏浜嬩欢',
+  'Message Intermediate Throw Event': '娑堟伅涓棿鎶涘嚭浜嬩欢',
+  'Timer Intermediate Catch Event': '瀹氭椂涓棿鎹曡幏浜嬩欢',
+  'Escalation Intermediate Throw Event': '鍗囩骇涓棿鎶涘嚭浜嬩欢',
+  'Conditional Intermediate Catch Event': '鏉′欢涓棿鎹曡幏浜嬩欢',
+  'Link Intermediate Catch Event': '閾炬帴涓棿鎹曡幏浜嬩欢',
+  'Link Intermediate Throw Event': '閾炬帴涓棿鎶涘嚭浜嬩欢',
+  'Compensation Intermediate Throw Event': '琛ュ伩涓棿鎶涘嚭浜嬩欢',
+  'Signal Intermediate Catch Event': '淇″彿涓棿鎹曡幏浜嬩欢',
+  'Signal Intermediate Throw Event': '淇″彿涓棿鎶涘嚭浜嬩欢',
+  'Message End Event': '娑堟伅缁撴潫浜嬩欢',
+  'Escalation End Event': '瀹氭椂缁撴潫浜嬩欢',
+  'Error End Event': '閿欒缁撴潫浜嬩欢',
+  'Cancel End Event': '鍙栨秷缁撴潫浜嬩欢',
+  'Compensation End Event': '琛ュ伩缁撴潫浜嬩欢',
+  'Signal End Event': '淇″彿缁撴潫浜嬩欢',
+  'Terminate End Event': '缁堟缁撴潫浜嬩欢',
+  'Message Boundary Event': '娑堟伅杈圭晫浜嬩欢',
+  'Message Boundary Event (non-interrupting)': '娑堟伅杈圭晫浜嬩欢锛堥潪涓柇锛�',
+  'Timer Boundary Event': '瀹氭椂杈圭晫浜嬩欢',
+  'Timer Boundary Event (non-interrupting)': '瀹氭椂杈圭晫浜嬩欢锛堥潪涓柇锛�',
+  'Escalation Boundary Event': '鍗囩骇杈圭晫浜嬩欢',
+  'Escalation Boundary Event (non-interrupting)': '鍗囩骇杈圭晫浜嬩欢锛堥潪涓柇锛�',
+  'Conditional Boundary Event': '鏉′欢杈圭晫浜嬩欢',
+  'Conditional Boundary Event (non-interrupting)': '鏉′欢杈圭晫浜嬩欢锛堥潪涓柇锛�',
+  'Error Boundary Event': '閿欒杈圭晫浜嬩欢',
+  'Cancel Boundary Event': '鍙栨秷杈圭晫浜嬩欢',
+  'Signal Boundary Event': '淇″彿杈圭晫浜嬩欢',
+  'Signal Boundary Event (non-interrupting)': '淇″彿杈圭晫浜嬩欢锛堥潪涓柇锛�',
+  'Compensation Boundary Event': '琛ュ伩杈圭晫浜嬩欢',
+  'Exclusive Gateway': '浜掓枼缃戝叧',
+  'Parallel Gateway': '骞惰缃戝叧',
+  'Inclusive Gateway': '鐩稿缃戝叧',
+  'Complex Gateway': '澶嶆潅缃戝叧',
+  'Event based Gateway': '浜嬩欢缃戝叧',
+  Transaction: '杞繍',
+  'Sub Process': '瀛愭祦绋�',
+  'Event Sub Process': '浜嬩欢瀛愭祦绋�',
+  'Collapsed Pool': '鎶樺彔姹�',
+  'Expanded Pool': '灞曞紑姹�',
+
+  // Errors
+  'no parent for {element} in {parent}': '鍦▄parent}閲岋紝{element}娌℃湁鐖剁被',
+  'no shape type specified': '娌℃湁鎸囧畾鐨勫舰鐘剁被鍨�',
+  'flow elements must be children of pools/participants': '娴佸厓绱犲繀椤绘槸姹�/鍙備笌鑰呯殑瀛愮被',
+  'out of bounds release': 'out of bounds release',
+  'more than {count} child lanes': '瀛愰亾澶т簬{count} ',
+  'element required': '鍏冪礌涓嶈兘涓虹┖',
+  'diagram not part of bpmn:Definitions': '娴佺▼鍥句笉绗﹀悎bpmn瑙勮寖',
+  'no diagram to display': '娌℃湁鍙睍绀虹殑娴佺▼鍥�',
+  'no process or collaboration to display': '娌℃湁鍙睍绀虹殑娴佺▼/鍗忎綔',
+  'element {element} referenced by {referenced}#{property} not yet drawn': '鐢眥referenced}#{property}寮曠敤鐨剓element}鍏冪礌浠嶆湭缁樺埗',
+  'already rendered {element}': '{element} 宸茶娓叉煋',
+  'failed to import {element}': '瀵煎叆{element}澶辫触',
+  // 灞炴�ч潰鏉跨殑鍙傛暟
+  Id: '缂栧彿',
+  Name: '鍚嶇О',
+  General: '甯歌',
+  Details: '璇︽儏',
+  'Message Name': '娑堟伅鍚嶇О',
+  Message: '娑堟伅',
+  Initiator: '鍒涘缓鑰�',
+  'Asynchronous Continuations': '鎸佺画寮傛',
+  'Asynchronous Before': '寮傛鍓�',
+  'Asynchronous After': '寮傛鍚�',
+  'Job Configuration': '宸ヤ綔閰嶇疆',
+  Exclusive: '鎺掗櫎',
+  'Job Priority': '宸ヤ綔浼樺厛绾�',
+  'Retry Time Cycle': '閲嶈瘯鏃堕棿鍛ㄦ湡',
+  Documentation: '鏂囨。',
+  'Element Documentation': '鍏冪礌鏂囨。',
+  'History Configuration': '鍘嗗彶閰嶇疆',
+  'History Time To Live': '鍘嗗彶鐨勭敓瀛樻椂闂�',
+  Forms: '琛ㄥ崟',
+  'Form Key': '琛ㄥ崟key',
+  'Form Fields': '琛ㄥ崟瀛楁',
+  'Business Key': '涓氬姟key',
+  'Form Field': '琛ㄥ崟瀛楁',
+  ID: '缂栧彿',
+  Type: '绫诲瀷',
+  Label: '鍚嶇О',
+  'Default Value': '榛樿鍊�',
+  'Default Flow': '榛樿娴佽浆璺緞',
+  'Conditional Flow': '鏉′欢娴佽浆璺緞',
+  'Sequence Flow': '鏅�氭祦杞矾寰�',
+  Validation: '鏍¢獙',
+  'Add Constraint': '娣诲姞绾︽潫',
+  Config: '閰嶇疆',
+  Properties: '灞炴��',
+  'Add Property': '娣诲姞灞炴��',
+  Value: '鍊�',
+  Listeners: '鐩戝惉鍣�',
+  'Execution Listener': '鎵ц鐩戝惉',
+  'Event Type': '浜嬩欢绫诲瀷',
+  'Listener Type': '鐩戝惉鍣ㄧ被鍨�',
+  'Java Class': 'Java绫�',
+  Expression: '琛ㄨ揪寮�',
+  'Must provide a value': '蹇呴』鎻愪緵涓�涓��',
+  'Delegate Expression': '浠g悊琛ㄨ揪寮�',
+  Script: '鑴氭湰',
+  'Script Format': '鑴氭湰鏍煎紡',
+  'Script Type': '鑴氭湰绫诲瀷',
+  'Inline Script': '鍐呰仈鑴氭湰',
+  'External Script': '澶栭儴鑴氭湰',
+  Resource: '璧勬簮',
+  'Field Injection': '瀛楁娉ㄥ叆',
+  Extensions: '鎵╁睍',
+  'Input/Output': '杈撳叆/杈撳嚭',
+  'Input Parameters': '杈撳叆鍙傛暟',
+  'Output Parameters': '杈撳嚭鍙傛暟',
+  Parameters: '鍙傛暟',
+  'Output Parameter': '杈撳嚭鍙傛暟',
+  'Timer Definition Type': '瀹氭椂鍣ㄥ畾涔夌被鍨�',
+  'Timer Definition': '瀹氭椂鍣ㄥ畾涔�',
+  Date: '鏃ユ湡',
+  Duration: '鎸佺画',
+  Cycle: '寰幆',
+  Signal: '淇″彿',
+  'Signal Name': '淇″彿鍚嶇О',
+  Escalation: '鍗囩骇',
+  Error: '閿欒',
+  'Link Name': '閾炬帴鍚嶇О',
+  Condition: '鏉′欢鍚嶇О',
+  'Variable Name': '鍙橀噺鍚嶇О',
+  'Variable Event': '鍙橀噺浜嬩欢',
+  'Specify more than one variable change event as a comma separated list.': '澶氫釜鍙橀噺浜嬩欢浠ラ�楀彿闅斿紑',
+  'Wait for Completion': '绛夊緟瀹屾垚',
+  'Activity Ref': '娲诲姩鍙傝��',
+  'Version Tag': '鐗堟湰鏍囩',
+  Executable: '鍙墽琛屾枃浠�',
+  'External Task Configuration': '鎵╁睍浠诲姟閰嶇疆',
+  'Task Priority': '浠诲姟浼樺厛绾�',
+  External: '澶栭儴',
+  Connector: '杩炴帴鍣�',
+  'Must configure Connector': '蹇呴』閰嶇疆杩炴帴鍣�',
+  'Connector Id': '杩炴帴鍣ㄧ紪鍙�',
+  Implementation: '瀹炵幇鏂瑰紡',
+  'Field Injections': '瀛楁娉ㄥ叆',
+  Fields: '瀛楁',
+  'Result Variable': '缁撴灉鍙橀噺',
+  Topic: '涓婚',
+  'Configure Connector': '閰嶇疆杩炴帴鍣�',
+  'Input Parameter': '杈撳叆鍙傛暟',
+  Assignee: '浠g悊浜�',
+  'Candidate Users': '鍊欓�夌敤鎴�',
+  'Candidate Groups': '鍊欓�夌粍',
+  'Due Date': '鍒版湡鏃堕棿',
+  'Follow Up Date': '璺熻釜鏃ユ湡',
+  Priority: '浼樺厛绾�',
+  'The follow up date as an EL expression (e.g. ${someDate} or an ISO date (e.g. 2015-06-26T09:54:00)':
+    '璺熻釜鏃ユ湡蹇呴』绗﹀悎EL琛ㄨ揪寮忥紝濡傦細 ${someDate} ,鎴栬�呬竴涓狪SO鏍囧噯鏃ユ湡锛屽锛�2015-06-26T09:54:00',
+  'The due date as an EL expression (e.g. ${someDate} or an ISO date (e.g. 2015-06-26T09:54:00)':
+    '璺熻釜鏃ユ湡蹇呴』绗﹀悎EL琛ㄨ揪寮忥紝濡傦細 ${someDate} ,鎴栬�呬竴涓狪SO鏍囧噯鏃ユ湡锛屽锛�2015-06-26T09:54:00',
+  Variables: '鍙橀噺',
+  'Candidate Starter Configuration': '鍊欓�変汉璧峰姩鍣ㄩ厤缃�',
+  'Candidate Starter Groups': '鍊欓�変汉璧峰姩鍣ㄧ粍',
+  'This maps to the process definition key.': '杩欐槧灏勫埌娴佺▼瀹氫箟閿��',
+  'Candidate Starter Users': '鍊欓�変汉璧峰姩鍣ㄧ殑鐢ㄦ埛',
+  'Specify more than one user as a comma separated list.': '鎸囧畾澶氫釜鐢ㄦ埛浣滀负閫楀彿鍒嗛殧鐨勫垪琛ㄣ��',
+  'Tasklist Configuration': 'Tasklist閰嶇疆',
+  Startable: '鍚姩',
+  'Specify more than one group as a comma separated list.': '鎸囧畾澶氫釜缁勪綔涓洪�楀彿鍒嗛殧鐨勫垪琛ㄣ��'
+};
diff --git a/ruoyi-ui/apps/web-antd/src/package/index.js b/ruoyi-ui/apps/web-antd/src/package/index.js
new file mode 100644
index 0000000..62532f5
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/index.js
@@ -0,0 +1,21 @@
+import MyProcessDesigner from './designer';
+import MyProcessPalette from './palette';
+import MyProcessPenal from './penal';
+
+const components = [MyProcessDesigner, MyProcessPenal, MyProcessPalette];
+
+const install = function(Vue) {
+  components.forEach(component => {
+    Vue.component(component.name, component);
+  });
+};
+
+if (typeof window !== 'undefined' && window.Vue) {
+  install(window.Vue);
+}
+
+export default {
+  version: '0.0.1',
+  install,
+  ...components
+};
diff --git a/ruoyi-ui/apps/web-antd/src/package/palette/ProcessPalette.vue b/ruoyi-ui/apps/web-antd/src/package/palette/ProcessPalette.vue
new file mode 100644
index 0000000..5fa1415
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/palette/ProcessPalette.vue
@@ -0,0 +1,106 @@
+<template>
+  <div class="my-process-palette">
+    <p>绠�鏄損alette</p>
+    <el-collapse>
+      <el-collapse-item title="浠诲姟" name="1">
+        <!--  鍙互绠�鍖栥�傘�傘�� -->
+        <div class="custom-button" @click="createElement($event, 'Task')" @mousedown="createElement($event, 'Task')">
+          浠诲姟
+        </div>
+        <div class="custom-button" @click="createElement($event, 'UserTask')" @mousedown="createElement($event, 'UserTask')">
+          鐢ㄦ埛浠诲姟
+        </div>
+        <div class="custom-button" @click="createElement($event, 'SendTask')" @mousedown="createElement($event, 'SendTask')">
+          鍙戦�佷换鍔�
+        </div>
+        <div class="custom-button" @click="createElement($event, 'ReceiveTask')" @mousedown="createElement($event, 'ReceiveTask')">
+          鎺ユ敹浠诲姟
+        </div>
+        <div class="custom-button" @click="createElement($event, 'ScriptTask')" @mousedown="createElement($event, 'ScriptTask')">
+          鑴氭湰浠诲姟
+        </div>
+        <div class="custom-button" @click="createElement($event, 'ServiceTask')" @mousedown="createElement($event, 'ServiceTask')">
+          鏈嶅姟浠诲姟
+        </div>
+      </el-collapse-item>
+      <el-collapse-item title="缃戝叧" name="2">
+        <div class="custom-button" @click="createElement($event, 'Gateway')" @mousedown="createElement($event, 'Gateway')">
+          缃戝叧
+        </div>
+      </el-collapse-item>
+      <el-collapse-item title="寮�濮�" name="3">
+        <div class="custom-button" @click="createElement($event, 'StartEvent')" @mousedown="createElement($event, 'StartEvent')">
+          寮�濮�
+        </div>
+      </el-collapse-item>
+      <el-collapse-item title="缁撴潫" name="4">
+        <div class="custom-button" @click="createElement($event, 'EndEvent')" @mousedown="createElement($event, 'EndEvent')">
+          缁撴潫
+        </div>
+      </el-collapse-item>
+      <el-collapse-item title="宸ュ叿" name="5">
+        <div class="custom-button" @click="startTool($event, 'handTool')" @mousedown="startTool($event, 'handTool')">
+          鎵嬪瀷宸ュ叿
+        </div>
+        <div class="custom-button" @click="startTool($event, 'lassoTool')" @mousedown="startTool($event, 'lassoTool')">
+          妗嗛�夊伐鍏�
+        </div>
+        <div class="custom-button" @click="startTool($event, 'connectTool')" @mousedown="startTool($event, 'connectTool')">
+          杩炵嚎宸ュ叿
+        </div>
+      </el-collapse-item>
+    </el-collapse>
+  </div>
+</template>
+
+<script>
+import { assign } from 'min-dash';
+
+export default {
+  name: 'MyProcessPalette',
+  data() {
+    return {};
+  },
+  mounted() {},
+  methods: {
+    createElement(event, type, options = {}) {
+      const ElementFactory = window.bpmnInstances.elementFactory;
+      const create = window.bpmnInstances.modeler.get('create');
+      const shape = ElementFactory.createShape(assign({ type: `bpmn:${type}` }, options));
+      if (options) {
+        shape.businessObject.di.isExpanded = options.isExpanded;
+      }
+      create.start(event, shape);
+    },
+    startTool(event, type) {
+      if (type === 'handTool') {
+        window.bpmnInstances.modeler.get('handTool').activateHand(event);
+      }
+      if (type === 'lassoTool') {
+        window.bpmnInstances.modeler.get('lassoTool').activateSelection(event);
+      }
+      if (type === 'connectTool') {
+        window.bpmnInstances.modeler.get('globalConnect').toggle(event);
+      }
+    }
+  }
+};
+</script>
+
+<style scoped lang="scss">
+.my-process-palette {
+  box-sizing: border-box;
+  padding: 8px;
+  .custom-button {
+    box-sizing: border-box;
+    padding: 4px 8px;
+    border-radius: 4px;
+    border: 1px solid rgba(24, 144, 255, 0.8);
+    cursor: pointer;
+    margin-bottom: 8px;
+    &:first-child {
+      margin-top: 8px;
+    }
+  }
+}
+</style>
diff --git a/ruoyi-ui/apps/web-antd/src/package/palette/index.js b/ruoyi-ui/apps/web-antd/src/package/palette/index.js
new file mode 100644
index 0000000..1e94b22
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/palette/index.js
@@ -0,0 +1,7 @@
+import MyPropertiesPalette from './ProcessPalette.vue';
+
+MyPropertiesPalette.install = function(Vue) {
+  Vue.component(MyPropertiesPalette.name, MyPropertiesPalette);
+};
+
+export default MyPropertiesPalette;
diff --git a/ruoyi-ui/apps/web-antd/src/package/penal/PropertiesPanel.vue b/ruoyi-ui/apps/web-antd/src/package/penal/PropertiesPanel.vue
new file mode 100644
index 0000000..8e04309
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/penal/PropertiesPanel.vue
@@ -0,0 +1,216 @@
+<template>
+  <div class="process-panel__container" :style="{ width: `${this.width}px` }">
+    <el-collapse v-model="activeTab">
+      <el-collapse-item name="base">
+        <template #title>
+          <div class="panel-tab__title"><el-icon><info-filled /></el-icon>甯歌</div>
+        </template>
+        <element-base-info :id-edit-disabled="idEditDisabled" :business-object="elementBusinessObject" :type="elementType" />
+      </el-collapse-item>
+      <el-collapse-item name="condition" v-if="elementType === 'Process'" key="message">
+        <template #title>
+          <div class="panel-tab__title"><el-icon><comment /></el-icon>娑堟伅涓庝俊鍙�</div>
+        </template>
+        <signal-and-massage />
+      </el-collapse-item>
+      <el-collapse-item name="condition" v-if="conditionFormVisible" key="condition">
+        <template #title>
+          <div class="panel-tab__title"><el-icon><promotion /></el-icon>娴佽浆鏉′欢</div>
+        </template>
+        <flow-condition :business-object="elementBusinessObject" :type="elementType" />
+      </el-collapse-item>
+      <el-collapse-item name="condition" v-if="formVisible" key="form">
+        <template #title>
+          <div class="panel-tab__title"><el-icon><list /></el-icon>琛ㄥ崟</div>
+        </template>
+        <element-form :id="elementId" :type="elementType" />
+      </el-collapse-item>
+      <el-collapse-item name="task" v-if="elementType.indexOf('Task') !== -1" key="task">
+        <template #title>
+          <div class="panel-tab__title"><el-icon><checked /></el-icon>浠诲姟</div>
+        </template>
+        <element-task :id="elementId" :type="elementType" />
+      </el-collapse-item>
+      <el-collapse-item name="multiInstance" v-if="elementType.indexOf('Task') !== -1" key="multiInstance">
+        <template #title>
+          <div class="panel-tab__title"><el-icon><help-filled /></el-icon>澶氬疄渚�</div>
+        </template>
+        <element-multi-instance :business-object="elementBusinessObject" :type="elementType" />
+      </el-collapse-item>
+      <el-collapse-item name="taskListeners" v-if="elementType === 'UserTask'" key="taskListeners">
+        <template #title>
+          <div class="panel-tab__title"><el-icon><bell-filled /></el-icon>浠诲姟鐩戝惉鍣�</div>
+        </template>
+        <user-task-listeners :id="elementId" :type="elementType" />
+      </el-collapse-item>
+      <el-collapse-item name="listeners" key="listeners">
+        <template #title>
+          <div class="panel-tab__title"><el-icon><bell-filled /></el-icon>鎵ц鐩戝惉鍣�</div>
+        </template>
+        <element-listeners :id="elementId" :type="elementType" />
+      </el-collapse-item>
+      <el-collapse-item name="extensions" key="extensions">
+        <template #title>
+          <div class="panel-tab__title"><el-icon><circle-plus /></el-icon>鎵╁睍灞炴��</div>
+        </template>
+        <element-properties :id="elementId" :type="elementType" />
+      </el-collapse-item>
+      <el-collapse-item name="other" key="other">
+        <template #title>
+
+          <div class="panel-tab__title"><el-icon><promotion /></el-icon>鍏朵粬</div>
+        </template>
+        <element-other-config :id="elementId" />
+      </el-collapse-item>
+    </el-collapse>
+  </div>
+</template>
+<script>
+import ElementBaseInfo from "./base/ElementBaseInfo.vue";
+import ElementOtherConfig from "./other/ElementOtherConfig.vue";
+import ElementTask from "./task/ElementTask.vue";
+import ElementMultiInstance from "./multi-instance/ElementMultiInstance.vue";
+import FlowCondition from "./flow-condition/FlowCondition.vue";
+import SignalAndMassage from "./signal-message/SignalAndMessage.vue";
+import ElementListeners from "./listeners/ElementListeners.vue";
+import ElementProperties from "./properties/ElementProperties.vue";
+import ElementForm from "./form/ElementForm.vue";
+import UserTaskListeners from "./listeners/UserTaskListeners.vue";
+import Log from "../Log";
+/**
+ * 渚ц竟鏍�
+ * @Author MiyueFE
+import ElementProperties from "./properties/ElementProperties";
+import ElementForm from "./form/ElementForm";
+import UserTaskListeners from "./listeners/UserTaskListeners";
+import Log from "../Log";
+/**
+ * 渚ц竟鏍�
+ * @Author MiyueFE
+ * @Home https://github.com/miyuesc
+ * @Date 2021骞�3鏈�31鏃�18:57:51
+ */
+export default {
+  name: "MyPropertiesPanel",
+  components: {
+    UserTaskListeners,
+    ElementForm,
+    ElementProperties,
+    ElementListeners,
+    SignalAndMassage,
+    FlowCondition,
+    ElementMultiInstance,
+    ElementTask,
+    ElementOtherConfig,
+    ElementBaseInfo
+  },
+  componentName: "MyPropertiesPanel",
+  props: {
+    bpmnModeler: Object,
+    prefix: {
+      type: String,
+      default: "camunda"
+    },
+    width: {
+      type: Number,
+      default: 480
+    },
+    idEditDisabled: {
+      type: Boolean,
+      default: false
+    }
+  },
+  provide() {
+    return {
+      prefix: this.prefix,
+      width: this.width
+    };
+  },
+  data() {
+    return {
+      activeTab: "base",
+      elementId: "",
+      elementType: "",
+      elementBusinessObject: {}, // 鍏冪礌 businessObject 闀滃儚锛屾彁渚涚粰闇�瑕佸仛鍒ゆ柇鐨勭粍浠朵娇鐢�
+      conditionFormVisible: false, // 娴佽浆鏉′欢璁剧疆
+      formVisible: false // 琛ㄥ崟閰嶇疆
+    };
+  },
+  watch: {
+    elementId: {
+      handler() {
+        this.activeTab = "base";
+      }
+    }
+  },
+  created() {
+    this.initModels();
+  },
+  methods: {
+    initModels() {
+      // 鍒濆鍖� modeler 浠ュ強鍏朵粬 moddle
+      if (!this.bpmnModeler) {
+        // 閬垮厤鍔犺浇鏃� 娴佺▼鍥� 骞舵湭鍔犺浇瀹屾垚
+        this.timer = setTimeout(() => this.initModels(), 10);
+        return;
+      }
+      if (this.timer) clearTimeout(this.timer);
+      window.bpmnInstances = {
+        modeler: this.bpmnModeler,
+        modeling: this.bpmnModeler.get("modeling"),
+        moddle: this.bpmnModeler.get("moddle"),
+        eventBus: this.bpmnModeler.get("eventBus"),
+        bpmnFactory: this.bpmnModeler.get("bpmnFactory"),
+        elementFactory: this.bpmnModeler.get("elementFactory"),
+        elementRegistry: this.bpmnModeler.get("elementRegistry"),
+        replace: this.bpmnModeler.get("replace"),
+        selection: this.bpmnModeler.get("selection")
+      };
+      this.getActiveElement();
+    },
+    getActiveElement() {
+      // 鍒濆绗竴涓�変腑鍏冪礌 bpmn:Process
+      this.initFormOnChanged(null);
+      this.bpmnModeler.on("import.done", e => {
+        this.initFormOnChanged(null);
+      });
+      // 鐩戝惉閫夋嫨浜嬩欢锛屼慨鏀瑰綋鍓嶆縺娲荤殑鍏冪礌浠ュ強琛ㄥ崟
+      this.bpmnModeler.on("selection.changed", ({ newSelection }) => {
+        this.initFormOnChanged(newSelection[0] || null);
+      });
+      this.bpmnModeler.on("element.changed", ({ element }) => {
+        // 淇濊瘉 淇敼 "榛樿娴佽浆璺緞" 绫讳技闇�瑕佷慨鏀瑰涓厓绱犵殑浜嬩欢鍙戠敓鐨勬椂鍊欙紝鏇存柊琛ㄥ崟鐨勫厓绱犱笌鍘熼�変腑鍏冪礌涓嶄竴鑷淬��
+        if (element && element.id === this.elementId) {
+          this.initFormOnChanged(element);
+        }
+      });
+    },
+    // 鍒濆鍖栨暟鎹�
+    initFormOnChanged(element) {
+      let activatedElement = element;
+      if (!activatedElement) {
+        activatedElement =
+          window.bpmnInstances.elementRegistry.find(el => el.type === "bpmn:Process") ??
+          window.bpmnInstances.elementRegistry.find(el => el.type === "bpmn:Collaboration");
+      }
+      if (!activatedElement) return;
+      Log.printBack(`select element changed: id: ${activatedElement.id} , type: ${activatedElement.businessObject.$type}`);
+      Log.prettyInfo("businessObject", activatedElement.businessObject);
+      window.bpmnInstances.bpmnElement = activatedElement;
+      this.bpmnElement = activatedElement;
+      this.elementId = activatedElement.id;
+      this.elementType = activatedElement.type.split(":")[1] || "";
+      this.elementBusinessObject = JSON.parse(JSON.stringify(activatedElement.businessObject));
+      this.conditionFormVisible = !!(
+        this.elementType === "SequenceFlow" &&
+        activatedElement.source &&
+        activatedElement.source.type.indexOf("StartEvent") === -1
+      );
+      this.formVisible = this.elementType === "UserTask" || this.elementType === "StartEvent";
+    },
+    beforeUnmount() {
+      window.bpmnInstances = null;
+    }
+  }
+};
+</script>
diff --git a/ruoyi-ui/apps/web-antd/src/package/penal/base/ElementBaseInfo.vue b/ruoyi-ui/apps/web-antd/src/package/penal/base/ElementBaseInfo.vue
new file mode 100644
index 0000000..a6d1f9a
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/penal/base/ElementBaseInfo.vue
@@ -0,0 +1,80 @@
+<template>
+  <div class="panel-tab__content">
+    <el-form size="small" label-width="90px" @submit.prevent>
+      <el-form-item label="ID">
+        <el-input v-model="elementBaseInfo.id" :disabled="idEditDisabled" clearable @change="updateBaseInfo('id')" />
+      </el-form-item>
+      <el-form-item label="鍚嶇О">
+        <el-input v-model="elementBaseInfo.name" clearable @change="updateBaseInfo('name')" />
+      </el-form-item>
+      <!--娴佺▼鐨勫熀纭�灞炴��-->
+      <template v-if="elementBaseInfo.$type === 'bpmn:Process'">
+        <el-form-item label="鐗堟湰鏍囩">
+          <el-input v-model="elementBaseInfo.versionTag" clearable @change="updateBaseInfo('versionTag')" />
+        </el-form-item>
+        <el-form-item label="鍙墽琛�">
+          <el-switch v-model="elementBaseInfo.isExecutable" active-text="鏄�" inactive-text="鍚�" @change="updateBaseInfo('isExecutable')" />
+        </el-form-item>
+      </template>
+      <el-form-item v-if="elementBaseInfo.$type === 'bpmn:SubProcess'" label="鐘舵��">
+        <el-switch v-model="elementBaseInfo.isExpanded" active-text="灞曞紑" inactive-text="鎶樺彔" @change="updateBaseInfo('isExpanded')" />
+      </el-form-item>
+    </el-form>
+  </div>
+</template>
+<script>
+export default {
+  name: "ElementBaseInfo",
+  props: {
+    businessObject: Object,
+    type: String,
+    idEditDisabled: {
+      type: Boolean,
+      default: true
+    }
+  },
+  data() {
+    return {
+      elementBaseInfo: {}
+    };
+  },
+  watch: {
+    businessObject: {
+      immediate: false,
+      handler: function(val) {
+        if (val) {
+          this.$nextTick(() => this.resetBaseInfo());
+        }
+      }
+    }
+  },
+  methods: {
+    resetBaseInfo() {
+      this.bpmnElement = window?.bpmnInstances?.bpmnElement || {};
+      this.elementBaseInfo = JSON.parse(JSON.stringify(this.bpmnElement.businessObject));
+      if (this.elementBaseInfo && this.elementBaseInfo.$type === "bpmn:SubProcess") {
+        this.elementBaseInfo["isExpanded"] = this.elementBaseInfo.di?.isExpanded
+      }
+    },
+    updateBaseInfo(key) {
+      if (key === "id") {
+        window.bpmnInstances.modeling.updateProperties(this.bpmnElement, {
+          id: this.elementBaseInfo[key],
+          di: { id: `${this.elementBaseInfo[key]}_di` }
+        });
+        return;
+      }
+      if (key === "isExpanded") {
+        window?.bpmnInstances?.modeling.toggleCollapse(this.bpmnElement);
+        return;
+      }
+      const attrObj = Object.create(null);
+      attrObj[key] = this.elementBaseInfo[key];
+      window.bpmnInstances.modeling.updateProperties(this.bpmnElement, attrObj);
+    }
+  },
+  beforeUnmount() {
+    this.bpmnElement = null;
+  }
+};
+</script>
diff --git a/ruoyi-ui/apps/web-antd/src/package/penal/flow-condition/FlowCondition.vue b/ruoyi-ui/apps/web-antd/src/package/penal/flow-condition/FlowCondition.vue
new file mode 100644
index 0000000..d06eeb6
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/penal/flow-condition/FlowCondition.vue
@@ -0,0 +1,142 @@
+<template>
+  <div class="panel-tab__content">
+    <el-form :model="flowConditionForm" label-width="90px" size="small" @submit.prevent>
+      <el-form-item label="娴佽浆绫诲瀷">
+        <el-select v-model="flowConditionForm.type" @change="updateFlowType">
+          <el-option label="鏅�氭祦杞矾寰�" value="normal" />
+          <el-option label="榛樿娴佽浆璺緞" value="default" />
+          <el-option label="鏉′欢娴佽浆璺緞" value="condition" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="鏉′欢鏍煎紡" v-if="flowConditionForm.type === 'condition'" key="condition">
+        <el-select v-model="flowConditionForm.conditionType">
+          <el-option label="琛ㄨ揪寮�" value="expression" />
+          <el-option label="鑴氭湰" value="script" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="琛ㄨ揪寮�" v-if="flowConditionForm.conditionType && flowConditionForm.conditionType === 'expression'" key="express">
+        <el-input v-model="flowConditionForm.body" clearable @change="updateFlowCondition" />
+      </el-form-item>
+      <template v-if="flowConditionForm.conditionType && flowConditionForm.conditionType === 'script'">
+        <el-form-item label="鑴氭湰璇█" key="language">
+          <el-input v-model="flowConditionForm.language" clearable @change="updateFlowCondition" />
+        </el-form-item>
+        <el-form-item label="鑴氭湰绫诲瀷" key="scriptType">
+          <el-select v-model="flowConditionForm.scriptType">
+            <el-option label="鍐呰仈鑴氭湰" value="inlineScript" />
+            <el-option label="澶栭儴鑴氭湰" value="externalScript" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="鑴氭湰" v-if="flowConditionForm.scriptType === 'inlineScript'" key="body">
+          <el-input v-model="flowConditionForm.body" type="textarea" clearable @change="updateFlowCondition" />
+        </el-form-item>
+        <el-form-item label="璧勬簮鍦板潃" v-if="flowConditionForm.scriptType === 'externalScript'" key="resource">
+          <el-input v-model="flowConditionForm.resource" clearable @change="updateFlowCondition" />
+        </el-form-item>
+      </template>
+    </el-form>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'FlowCondition',
+  props: {
+    businessObject: Object,
+    type: String
+  },
+  data() {
+    return {
+      flowConditionForm: {}
+    };
+  },
+  watch: {
+    businessObject: {
+      immediate: true,
+      handler() {
+        this.$nextTick(() => this.resetFlowCondition());
+      }
+    }
+  },
+  methods: {
+    resetFlowCondition() {
+      this.bpmnElement = window.bpmnInstances.bpmnElement;
+      this.bpmnElementSource = this.bpmnElement.source;
+      this.bpmnElementSourceRef = this.bpmnElement.businessObject.sourceRef;
+      if (this.bpmnElementSourceRef && this.bpmnElementSourceRef.default && this.bpmnElementSourceRef.default.id === this.bpmnElement.id) {
+        // 榛樿
+        this.flowConditionForm = { type: 'default' };
+      } else if (!this.bpmnElement.businessObject.conditionExpression) {
+        // 鏅��
+        this.flowConditionForm = { type: 'normal' };
+      } else {
+        // 甯︽潯浠�
+        const conditionExpression = this.bpmnElement.businessObject.conditionExpression;
+        this.flowConditionForm = { ...conditionExpression, type: 'condition' };
+        // resource 鍙洿鎺ユ爣璇� 鏄惁鏄閮ㄨ祫婧愯剼鏈�
+        if (this.flowConditionForm.resource) {
+          this.flowConditionForm['conditionType'] = 'script'
+          this.flowConditionForm['scriptType'] = 'externalScript'
+          return;
+        }
+        if (conditionExpression.language) {
+          this.flowConditionForm['conditionType'] = 'script'
+          this.flowConditionForm['scriptType'] = 'inlineScript'
+          return;
+        }
+        this.flowConditionForm['conditionType'] = 'expression'
+      }
+    },
+    updateFlowType(flowType) {
+      // 姝e父鏉′欢绫�
+      if (flowType === 'condition') {
+        this.flowConditionRef = window.bpmnInstances.moddle.create('bpmn:FormalExpression');
+        window.bpmnInstances.modeling.updateProperties(this.bpmnElement, {
+          conditionExpression: this.flowConditionRef
+        });
+        return;
+      }
+      // 榛樿璺緞
+      if (flowType === 'default') {
+        window.bpmnInstances.modeling.updateProperties(this.bpmnElement, {
+          conditionExpression: null
+        });
+        window.bpmnInstances.modeling.updateProperties(this.bpmnElementSource, {
+          default: this.bpmnElement
+        });
+        return;
+      }
+      // 姝e父璺緞锛屽鏋滄潵婧愯妭鐐圭殑榛樿璺緞鏄綋鍓嶈繛绾挎椂锛屾竻闄ょ埗鍏冪礌鐨勯粯璁よ矾寰勯厤缃�
+      if (this.bpmnElementSourceRef.default && this.bpmnElementSourceRef.default.id === this.bpmnElement.id) {
+        window.bpmnInstances.modeling.updateProperties(this.bpmnElementSource, {
+          default: null
+        });
+      }
+      window.bpmnInstances.modeling.updateProperties(this.bpmnElement, {
+        conditionExpression: null
+      });
+    },
+    updateFlowCondition() {
+      const { conditionType, scriptType, body, resource, language } = this.flowConditionForm;
+      let condition;
+      if (conditionType === 'expression') {
+        condition = window.bpmnInstances.moddle.create('bpmn:FormalExpression', { body });
+      } else {
+        if (scriptType === 'inlineScript') {
+          condition = window.bpmnInstances.moddle.create('bpmn:FormalExpression', { body, language });
+          this.flowConditionForm['resource'] = ''
+        } else {
+          this.flowConditionForm['body'] = ''
+          condition = window.bpmnInstances.moddle.create('bpmn:FormalExpression', { resource, language });
+        }
+      }
+      window.bpmnInstances.modeling.updateProperties(this.bpmnElement, { conditionExpression: condition });
+    }
+  },
+  beforeUnmount() {
+    this.bpmnElement = null;
+    this.bpmnElementSource = null;
+    this.bpmnElementSourceRef = null;
+  }
+};
+</script>
diff --git a/ruoyi-ui/apps/web-antd/src/package/penal/form/ElementForm.vue b/ruoyi-ui/apps/web-antd/src/package/penal/form/ElementForm.vue
new file mode 100644
index 0000000..33628bf
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/penal/form/ElementForm.vue
@@ -0,0 +1,417 @@
+<template>
+  <div class="panel-tab__content">
+    <el-form size="small" label-width="80px"  @submit.prevent>
+      <el-form-item label="琛ㄥ崟" prop="formKey">
+        <el-select v-model="formKey" placeholder="璇烽�夋嫨琛ㄥ崟" @change="updateElementFormKey" clearable>
+          <el-option v-for="item in formOptions" :key="item.formId" :label="item.formName" :value="`key_${item.formId}`" />
+        </el-select>
+      </el-form-item>
+      <el-form-item prop="localScope">
+        <template #label>
+          <div class="flex align-center">
+            <el-tooltip content="鑻ヤ负鑺傜偣琛ㄥ崟锛屽垯琛ㄥ崟淇℃伅浠呭湪姝よ妭鐐瑰彲鐢紝榛樿涓哄叏灞�琛ㄥ崟锛岃〃鍗曚俊鎭湪鏁翠釜娴佺▼瀹炰緥涓彲鐢�" placement="top-start">
+              <el-icon><InfoFilled /></el-icon>
+            </el-tooltip>
+            <span>鑺傜偣琛ㄥ崟</span>
+          </div>
+        </template>
+        <el-switch
+          :disabled="type === 'StartEvent'"
+          v-model="localScope"
+          active-text="鏄�"
+          inactive-text="鍚�"
+          @change="updateElementFormScope()"
+        ></el-switch>
+      </el-form-item>
+      <!--      <el-form-item label="琛ㄥ崟鏍囪瘑">-->
+      <!--        <el-input v-model="formKey" clearable @change="updateElementFormKey" />-->
+      <!--      </el-form-item>-->
+      <!--      <el-form-item label="涓氬姟鏍囪瘑">-->
+      <!--        <el-select v-model="businessKey" @change="updateElementBusinessKey">-->
+      <!--          <el-option v-for="i in fieldList" :key="i.id" :value="i.id" :label="i.label" />-->
+      <!--          <el-option label="鏃�" value="" />-->
+      <!--        </el-select>-->
+      <!--      </el-form-item>-->
+    </el-form>
+
+    <!--瀛楁鍒楄〃-->
+    <!--    <div class="element-property list-property">-->
+    <!--      <el-divider><el-icon><coin /></el-icon> 琛ㄥ崟瀛楁</el-divider>-->
+    <!--      <el-table :data="fieldList" size="small" max-height="240" border fit>-->
+    <!--        <el-table-column label="搴忓彿" type="index" width="50px" />-->
+    <!--        <el-table-column label="瀛楁鍚嶇О" prop="label" min-width="80px" show-overflow-tooltip />-->
+    <!--        <el-table-column label="瀛楁绫诲瀷" prop="type" min-width="80px" :formatter="row => fieldType[row.type] || row.type" show-overflow-tooltip />-->
+    <!--        <el-table-column label="榛樿鍊�" prop="defaultValue" min-width="80px" show-overflow-tooltip />-->
+    <!--        <el-table-column label="鎿嶄綔" width="90px">-->
+    <!--          <template v-slot="{ row, $index }">-->
+    <!--            <el-button link type=""  @click="openFieldForm(row, $index)">缂栬緫</el-button>-->
+    <!--            <el-divider direction="vertical" />-->
+    <!--            <el-button link type=""  style="color: #ff4d4f" @click="removeField(row, $index)">绉婚櫎</el-button>-->
+    <!--          </template>-->
+    <!--        </el-table-column>-->
+    <!--      </el-table>-->
+    <!--    </div>-->
+    <!--    <div class="element-drawer__button">-->
+    <!--      <el-button size="small" type="primary" :icon="Plus"  @click="openFieldForm(null, -1)">娣诲姞瀛楁</el-button>-->
+    <!--    </div>-->
+
+    <!--瀛楁閰嶇疆渚ц竟鏍�-->
+    <!--    <el-drawer v-model="fieldModelVisible" title="瀛楁閰嶇疆" :size="`${width}px`" append-to-body destroy-on-close>-->
+    <!--      <el-form :model="formFieldForm" label-width="90px" size="small" @submit.prevent>-->
+    <!--        <el-form-item label="瀛楁ID">-->
+    <!--          <el-input v-model="formFieldForm.id" clearable />-->
+    <!--        </el-form-item>-->
+    <!--        <el-form-item label="绫诲瀷">-->
+    <!--          <el-select v-model="formFieldForm.typeType" placeholder="璇烽�夋嫨瀛楁绫诲瀷" clearable @change="changeFieldTypeType">-->
+    <!--            <el-option v-for="(value, key) of fieldType" :key="key" :label="value" :value="key" />-->
+    <!--          </el-select>-->
+    <!--        </el-form-item>-->
+    <!--        <el-form-item v-if="formFieldForm.typeType === 'custom'" label="绫诲瀷鍚嶇О">-->
+    <!--          <el-input v-model="formFieldForm.type" clearable />-->
+    <!--        </el-form-item>-->
+    <!--        <el-form-item label="鍚嶇О">-->
+    <!--          <el-input v-model="formFieldForm.label" clearable />-->
+    <!--        </el-form-item>-->
+    <!--        <el-form-item v-if="formFieldForm.typeType === 'date'" label="鏃堕棿鏍煎紡">-->
+    <!--          <el-input v-model="formFieldForm.datePattern" clearable />-->
+    <!--        </el-form-item>-->
+    <!--        <el-form-item label="榛樿鍊�">-->
+    <!--          <el-input v-model="formFieldForm.defaultValue" clearable />-->
+    <!--        </el-form-item>-->
+    <!--      </el-form>-->
+
+    <!-- 鏋氫妇鍊艰缃� -->
+    <!--      <template v-if="formFieldForm.type === 'enum'">-->
+    <!--        <el-divider key="enum-divider" />-->
+    <!--        <p key="enum-title" class="listener-filed__title">-->
+    <!--          <span>-->
+    <!--            <el-icon>-->
+    <!--               <menu />-->
+    <!--            </el-icon>-->
+    <!--          鏋氫妇鍊煎垪琛細-->
+    <!--          </span>-->
+    <!--          <el-button size="small" type="primary" @click="openFieldOptionForm(null, -1, 'enum')">娣诲姞鏋氫妇鍊�</el-button>-->
+    <!--        </p>-->
+    <!--        <el-table key="enum-table" :data="fieldEnumList" size="small" max-height="240" border fit>-->
+    <!--          <el-table-column label="搴忓彿" width="50px" type="index" />-->
+    <!--          <el-table-column label="鏋氫妇鍊肩紪鍙�" prop="id" min-width="100px" show-overflow-tooltip />-->
+    <!--          <el-table-column label="鏋氫妇鍊煎悕绉�" prop="name" min-width="100px" show-overflow-tooltip />-->
+    <!--          <el-table-column label="鎿嶄綔" width="90px">-->
+    <!--            <template v-slot="{ row, $index }">-->
+    <!--              <el-button link type="" @click="openFieldOptionForm(row, $index, 'enum')">缂栬緫</el-button>-->
+    <!--              <el-divider direction="vertical" />-->
+    <!--              <el-button link type="" style="color: #ff4d4f" @click="removeFieldOptionItem(row, $index, 'enum')">绉婚櫎</el-button>-->
+    <!--            </template>-->
+    <!--          </el-table-column>-->
+    <!--        </el-table>-->
+    <!--      </template>-->
+
+    <!-- 鏍¢獙瑙勫垯 -->
+    <!--      <el-divider key="validation-divider" />-->
+    <!--      <p key="validation-title" class="listener-filed__title">-->
+    <!--        <span><el-icon><menu /></el-icon>绾︽潫鏉′欢鍒楄〃锛�</span>-->
+    <!--        <el-button size="small" type="primary" @click="openFieldOptionForm(null, -1, 'constraint')">娣诲姞绾︽潫</el-button>-->
+    <!--      </p>-->
+    <!--      <el-table key="validation-table" :data="fieldConstraintsList" size="small" max-height="240" border fit>-->
+    <!--        <el-table-column label="搴忓彿" width="50px" type="index" />-->
+    <!--        <el-table-column label="绾︽潫鍚嶇О" prop="name" min-width="100px" show-overflow-tooltip />-->
+    <!--        <el-table-column label="绾︽潫閰嶇疆" prop="config" min-width="100px" show-overflow-tooltip />-->
+    <!--        <el-table-column label="鎿嶄綔" width="90px">-->
+    <!--          <template v-slot="{ row, $index }">-->
+    <!--            <el-button link type="" @click="openFieldOptionForm(row, $index, 'constraint')">缂栬緫</el-button>-->
+    <!--            <el-divider direction="vertical" />-->
+    <!--            <el-button link type="" style="color: #ff4d4f" @click="removeFieldOptionItem(row, $index, 'constraint')">绉婚櫎</el-button>-->
+    <!--          </template>-->
+    <!--        </el-table-column>-->
+    <!--      </el-table>-->
+
+    <!-- 琛ㄥ崟灞炴�� -->
+    <!--      <el-divider key="property-divider" />-->
+    <!--      <p key="property-title" class="listener-filed__title">-->
+    <!--        <span><el-icon><menu /></el-icon>瀛楁灞炴�у垪琛細</span>-->
+    <!--        <el-button size="small" type="primary" @click="openFieldOptionForm(null, -1, 'property')">娣诲姞灞炴��</el-button>-->
+    <!--      </p>-->
+    <!--      <el-table key="property-table" :data="fieldPropertiesList" size="small" max-height="240" border fit>-->
+    <!--        <el-table-column label="搴忓彿" width="50px" type="index" />-->
+    <!--        <el-table-column label="灞炴�х紪鍙�" prop="id" min-width="100px" show-overflow-tooltip />-->
+    <!--        <el-table-column label="灞炴�у��" prop="value" min-width="100px" show-overflow-tooltip />-->
+    <!--        <el-table-column label="鎿嶄綔" width="90px">-->
+    <!--          <template v-slot="{ row, $index }">-->
+    <!--            <el-button link type="" @click="openFieldOptionForm(row, $index, 'property')">缂栬緫</el-button>-->
+    <!--            <el-divider direction="vertical" />-->
+    <!--            <el-button link type="" style="color: #ff4d4f" @click="removeFieldOptionItem(row, $index, 'property')">绉婚櫎</el-button>-->
+    <!--          </template>-->
+    <!--        </el-table-column>-->
+    <!--      </el-table>-->
+
+    <!-- 搴曢儴鎸夐挳 -->
+    <!--      <div class="element-drawer__button">-->
+    <!--        <el-button size="small">鍙� 娑�</el-button>-->
+    <!--        <el-button size="small" type="primary" @click="saveField">淇� 瀛�</el-button>-->
+    <!--      </div>-->
+    <!--    </el-drawer>-->
+
+    <el-dialog v-model="fieldOptionModelVisible" :title="optionModelTitle" width="600px" append-to-body destroy-on-close>
+      <el-form :model="fieldOptionForm" size="small" label-width="96px" @submit.prevent>
+        <el-form-item v-if="fieldOptionType !== 'constraint'" key="option-id" label="缂栧彿/ID">
+          <el-input v-model="fieldOptionForm.id" clearable />
+        </el-form-item>
+        <el-form-item v-if="fieldOptionType !== 'property'" key="option-name" label="鍚嶇О">
+          <el-input v-model="fieldOptionForm.name" clearable />
+        </el-form-item>
+        <el-form-item v-if="fieldOptionType === 'constraint'" key="option-config" label="閰嶇疆">
+          <el-input v-model="fieldOptionForm.config" clearable />
+        </el-form-item>
+        <el-form-item v-if="fieldOptionType === 'property'" key="option-value" label="鍊�">
+          <el-input v-model="fieldOptionForm.value" clearable />
+        </el-form-item>
+      </el-form>
+      <template v-slot:footer>
+        <el-button size="small" @click="fieldOptionModelVisible = false">鍙� 娑�</el-button>
+        <el-button size="small" type="primary" @click="saveFieldOption">纭� 瀹�</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listForm } from "#/api/workflow/form";
+
+export default {
+  name: 'ElementForm',
+  setup() {
+    return {
+      Plus
+    }
+  },
+  props: {
+    id: String,
+    type: String
+  },
+  inject: {
+    prefix: 'prefix',
+    width: 'width'
+  },
+  data() {
+    return {
+      formKey: '',
+      formOptions: [],
+      localScope: false,
+      businessKey: '',
+      optionModelTitle: '',
+      fieldList: [],
+      formFieldForm: {},
+      fieldType: {
+        long: '闀挎暣鍨�',
+        string: '瀛楃涓�',
+        boolean: '甯冨皵绫�',
+        date: '鏃ユ湡绫�',
+        enum: '鏋氫妇绫�',
+        custom: '鑷畾涔夌被鍨�'
+      },
+      formFieldIndex: -1, // 缂栬緫涓殑瀛楁锛� -1 涓烘柊澧�
+      formFieldOptionIndex: -1, // 缂栬緫涓殑瀛楁閰嶇疆椤癸紝 -1 涓烘柊澧�
+      fieldModelVisible: false,
+      fieldOptionModelVisible: false,
+      fieldOptionForm: {}, // 褰撳墠婵�娲荤殑瀛楁閰嶇疆椤规暟鎹�
+      fieldOptionType: '', // 褰撳墠婵�娲荤殑瀛楁閰嶇疆椤瑰脊绐� 绫诲瀷
+      fieldEnumList: [], // 鏋氫妇鍊煎垪琛�
+      fieldConstraintsList: [], // 绾︽潫鏉′欢鍒楄〃
+      fieldPropertiesList: [] // 缁戝畾灞炴�у垪琛�
+    };
+  },
+  watch: {
+    id: {
+      immediate: true,
+      handler(val) {
+        val && val.length && this.$nextTick(() => this.resetFormList());
+      }
+    }
+  },
+  created() {
+    /** 鏌ヨ娴佺▼鍒嗙被鍒楄〃 */
+    this.getFormList();
+  },
+  methods: {
+    /** 鏌ヨ琛ㄥ崟鍒楄〃 */
+    getFormList() {
+      listForm().then(response => {
+        this.formOptions = response.rows;
+    }
+    )
+    },
+    resetFormList() {
+      this.bpmnELement = window.bpmnInstances.bpmnElement;
+      this.formKey = this.bpmnELement.businessObject.formKey;
+      this.localScope = this.bpmnELement.businessObject.localScope;
+      // 鑾峰彇鍏冪礌鎵╁睍灞炴�� 鎴栬�� 鍒涘缓鎵╁睍灞炴��
+      this.elExtensionElements =
+        this.bpmnELement.businessObject.get('extensionElements') || window.bpmnInstances.moddle.create('bpmn:ExtensionElements', { values: [] });
+      // 鑾峰彇鍏冪礌琛ㄥ崟閰嶇疆 鎴栬�� 鍒涘缓鏂扮殑琛ㄥ崟閰嶇疆
+      // try {
+      // this.formData =
+      //   this.elExtensionElements.values.filter(ex => ex.$type === `${this.prefix}:FormData`)[0] ||
+      //   window.bpmnInstances.moddle.create(`${this.prefix}:FormData`, { fields: [] });
+      // } catch (error) {
+      //   this.formData = {}
+      //   console.log(error)
+      // }
+
+      // 涓氬姟鏍囪瘑 businessKey锛� 缁戝畾鍦� formData 涓�
+      // this.businessKey = this.formData.businessKey;
+
+      // 淇濈暀鍓╀綑鎵╁睍鍏冪礌锛屼究浜庡悗闈㈡洿鏂拌鍏冪礌瀵瑰簲灞炴��
+      this.otherExtensions = this.elExtensionElements.values.filter(ex => ex.$type !== `${this.prefix}:FormData`);
+
+      // 澶嶅埗鍘熷鍊硷紝濉厖琛ㄦ牸
+      // this.fieldList = JSON.parse(JSON.stringify(this.formData.fields || []));
+
+      // 鏇存柊鍏冪礌鎵╁睍灞炴�э紝閬垮厤鍚庣画鎶ラ敊
+      // this.updateElementExtensions();
+    },
+    updateElementFormKey() {
+      window.bpmnInstances.modeling.updateProperties(this.bpmnELement, { formKey: this.formKey });
+    },
+    updateElementFormScope() {
+      window.bpmnInstances.modeling.updateProperties(this.bpmnELement, { localScope: this.localScope });
+    },
+    updateElementBusinessKey() {
+      window.bpmnInstances.modeling.updateModdleProperties(this.bpmnELement, this.formData, { businessKey: this.businessKey });
+    },
+    // 鏍规嵁绫诲瀷璋冩暣瀛楁type
+    changeFieldTypeType(type) {
+      this.formFieldForm['type'] = type === 'custom' ? '' : type
+    },
+
+    // 鎵撳紑瀛楁璇︽儏渚ц竟鏍�
+    openFieldForm(field, index) {
+      this.formFieldIndex = index;
+      if (index !== -1) {
+        const FieldObject = this.formData.fields[index];
+        this.formFieldForm = JSON.parse(JSON.stringify(field));
+        // 璁剧疆鑷畾涔夌被鍨�
+        this.formFieldForm['typeType'] = !this.fieldType[field.type] ? 'custom' : field.type
+        // 鍒濆鍖栨灇涓惧�煎垪琛�
+        field.type === 'enum' && (this.fieldEnumList = JSON.parse(JSON.stringify(FieldObject?.values || [])));
+        // 鍒濆鍖栫害鏉熸潯浠跺垪琛�
+        this.fieldConstraintsList = JSON.parse(JSON.stringify(FieldObject?.validation?.constraints || []));
+        // 鍒濆鍖栬嚜瀹氫箟灞炴�у垪琛�
+        this.fieldPropertiesList = JSON.parse(JSON.stringify(FieldObject?.properties?.values || []));
+      } else {
+        this.formFieldForm = {};
+        // 鍒濆鍖栨灇涓惧�煎垪琛�
+        this.fieldEnumList = [];
+        // 鍒濆鍖栫害鏉熸潯浠跺垪琛�
+        this.fieldConstraintsList = [];
+        // 鍒濆鍖栬嚜瀹氫箟灞炴�у垪琛�
+        this.fieldPropertiesList = [];
+      }
+      this.fieldModelVisible = true;
+    },
+    // 鎵撳紑瀛楁 鏌愪釜 閰嶇疆椤� 寮圭獥
+    openFieldOptionForm(option, index, type) {
+      this.fieldOptionModelVisible = true;
+      this.fieldOptionType = type;
+      this.formFieldOptionIndex = index;
+      if (type === 'property') {
+        this.fieldOptionForm = option ? JSON.parse(JSON.stringify(option)) : {};
+        return (this.optionModelTitle = '灞炴�ч厤缃�');
+      }
+      if (type === 'enum') {
+        this.fieldOptionForm = option ? JSON.parse(JSON.stringify(option)) : {};
+        return (this.optionModelTitle = '鏋氫妇鍊奸厤缃�');
+      }
+      this.fieldOptionForm = option ? JSON.parse(JSON.stringify(option)) : {};
+      return (this.optionModelTitle = '绾︽潫鏉′欢閰嶇疆');
+    },
+
+    // 淇濆瓨瀛楁 鏌愪釜 閰嶇疆椤�
+    saveFieldOption() {
+      if (this.formFieldOptionIndex === -1) {
+        if (this.fieldOptionType === 'property') {
+          this.fieldPropertiesList.push(this.fieldOptionForm);
+        }
+        if (this.fieldOptionType === 'constraint') {
+          this.fieldConstraintsList.push(this.fieldOptionForm);
+        }
+        if (this.fieldOptionType === 'enum') {
+          this.fieldEnumList.push(this.fieldOptionForm);
+        }
+      } else {
+        this.fieldOptionType === 'property' && this.fieldPropertiesList.splice(this.formFieldOptionIndex, 1, this.fieldOptionForm);
+        this.fieldOptionType === 'constraint' && this.fieldConstraintsList.splice(this.formFieldOptionIndex, 1, this.fieldOptionForm);
+        this.fieldOptionType === 'enum' && this.fieldEnumList.splice(this.formFieldOptionIndex, 1, this.fieldOptionForm);
+      }
+      this.fieldOptionModelVisible = false;
+      this.fieldOptionForm = {};
+    },
+    // 淇濆瓨瀛楁閰嶇疆
+    saveField() {
+      const { id, type, label, defaultValue, datePattern } = this.formFieldForm;
+      const Field = window.bpmnInstances.moddle.create(`${this.prefix}:FormField`, { id, type, label });
+      defaultValue && (Field.defaultValue = defaultValue);
+      datePattern && (Field.datePattern = datePattern);
+      // 鏋勫缓灞炴��
+      if (this.fieldPropertiesList && this.fieldPropertiesList.length) {
+        const fieldPropertyList = this.fieldPropertiesList.map(fp => {
+          return window.bpmnInstances.moddle.create(`${this.prefix}:Property`, { id: fp.id, value: fp.value });
+        });
+        Field.properties = window.bpmnInstances.moddle.create(`${this.prefix}:Properties`, { values: fieldPropertyList });
+      }
+      // 鏋勫缓鏍¢獙瑙勫垯
+      if (this.fieldConstraintsList && this.fieldConstraintsList.length) {
+        const fieldConstraintList = this.fieldConstraintsList.map(fc => {
+          return window.bpmnInstances.moddle.create(`${this.prefix}:Constraint`, { name: fc.name, config: fc.config });
+        });
+        Field.validation = window.bpmnInstances.moddle.create(`${this.prefix}:Validation`, { constraints: fieldConstraintList });
+      }
+      // 鏋勫缓鏋氫妇鍊�
+      if (this.fieldEnumList && this.fieldEnumList.length) {
+        Field.values = this.fieldEnumList.map(fe => {
+          return window.bpmnInstances.moddle.create(`${this.prefix}:Value`, { name: fe.name, id: fe.id });
+        });
+      }
+      // 鏇存柊鏁扮粍 涓� 琛ㄥ崟閰嶇疆瀹炰緥
+      if (this.formFieldIndex === -1) {
+        this.fieldList.push(this.formFieldForm);
+        this.formData.fields && this.formData.fields.push(Field);
+      } else {
+        this.fieldList.splice(this.formFieldIndex, 1, this.formFieldForm);
+        this.formData.fields.splice(this.formFieldIndex, 1, Field);
+      }
+      this.updateElementExtensions();
+      this.fieldModelVisible = false;
+    },
+
+    // 绉婚櫎鏌愪釜 瀛楁鐨� 閰嶇疆椤�
+    removeFieldOptionItem(option, index, type) {
+      if (type === 'property') {
+        this.fieldPropertiesList.splice(index, 1);
+        return;
+      }
+      if (type === 'enum') {
+        this.fieldEnumList.splice(index, 1);
+        return;
+      }
+      this.fieldConstraintsList.splice(index, 1);
+    },
+    // 绉婚櫎 瀛楁
+    removeField(field, index) {
+      this.fieldList.splice(index, 1);
+      this.formData.fields.splice(index, 1);
+      this.updateElementExtensions();
+    },
+
+    updateElementExtensions() {
+      // 鏇存柊鍥炴墿灞曞厓绱�
+      const newElExtensionElements = window.bpmnInstances.moddle.create(`bpmn:ExtensionElements`, {
+        values: this.otherExtensions.concat(this.formData)
+      });
+      // 鏇存柊鍒板厓绱犱笂
+      window.bpmnInstances.modeling.updateProperties(this.bpmnELement, {
+        extensionElements: newElExtensionElements
+      });
+    }
+  }
+}
+</script>
diff --git a/ruoyi-ui/apps/web-antd/src/package/penal/index.js b/ruoyi-ui/apps/web-antd/src/package/penal/index.js
new file mode 100644
index 0000000..873d555
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/penal/index.js
@@ -0,0 +1,7 @@
+import MyPropertiesPanel from './PropertiesPanel.vue';
+
+MyPropertiesPanel.install = function(Vue) {
+  Vue.component(MyPropertiesPanel.name, MyPropertiesPanel);
+};
+
+export default MyPropertiesPanel;
diff --git a/ruoyi-ui/apps/web-antd/src/package/penal/listeners/ElementListeners.vue b/ruoyi-ui/apps/web-antd/src/package/penal/listeners/ElementListeners.vue
new file mode 100644
index 0000000..55f7d3c
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/penal/listeners/ElementListeners.vue
@@ -0,0 +1,302 @@
+<template>
+  <div class="panel-tab__content">
+    <el-table :data="elementListenersList" size="small" border>
+      <el-table-column label="搴忓彿" width="50px" type="index" />
+      <el-table-column label="浜嬩欢绫诲瀷" min-width="100px" prop="event" />
+      <el-table-column label="鐩戝惉鍣ㄧ被鍨�" min-width="100px" show-overflow-tooltip :formatter="row => listenerTypeObject[row.listenerType]" />
+      <el-table-column label="鎿嶄綔" width="90px">
+        <template v-slot="{ row, $index }">
+          <el-button link type="" @click="openListenerForm(row, $index)">缂栬緫</el-button>
+          <el-divider direction="vertical" />
+          <el-button link type="" style="color: #ff4d4f" @click="removeListener(row, $index)">绉婚櫎</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <div class="element-drawer__button">
+      <el-button size="small" type="primary" :icon="Plus" @click="openListenerForm(null)">娣诲姞鐩戝惉鍣�</el-button>
+    </div>
+
+    <!-- 鐩戝惉鍣� 缂栬緫/鍒涘缓 閮ㄥ垎 -->
+    <el-drawer v-model="listenerFormModelVisible" title="鎵ц鐩戝惉鍣�" :size="`${width}px`" append-to-body destroy-on-close>
+      <el-form size="small" :model="listenerForm" label-width="96px" ref="listenerFormRef" @submit.prevent>
+        <el-form-item label="浜嬩欢绫诲瀷" prop="event" :rules="{ required: true, trigger: ['blur', 'change'] }">
+          <el-select v-model="listenerForm.event">
+            <el-option label="start" value="start" />
+            <el-option label="end" value="end" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="鐩戝惉鍣ㄧ被鍨�" prop="listenerType" :rules="{ required: true, trigger: ['blur', 'change'] }">
+          <el-select v-model="listenerForm.listenerType">
+            <el-option v-for="i in Object.keys(listenerTypeObject)" :key="i" :label="listenerTypeObject[i]" :value="i" />
+          </el-select>
+        </el-form-item>
+        <el-form-item
+          v-if="listenerForm.listenerType === 'classListener'"
+          label="Java绫�"
+          prop="class"
+          key="listener-class"
+          :rules="{ required: true, trigger: ['blur', 'change'] }"
+        >
+          <el-input v-model="listenerForm.class" clearable />
+        </el-form-item>
+        <el-form-item
+          v-if="listenerForm.listenerType === 'expressionListener'"
+          label="琛ㄨ揪寮�"
+          prop="expression"
+          key="listener-expression"
+          :rules="{ required: true, trigger: ['blur', 'change'] }"
+        >
+          <el-input v-model="listenerForm.expression" clearable />
+        </el-form-item>
+        <el-form-item
+          v-if="listenerForm.listenerType === 'delegateExpressionListener'"
+          label="浠g悊琛ㄨ揪寮�"
+          prop="delegateExpression"
+          key="listener-delegate"
+          :rules="{ required: true, trigger: ['blur', 'change'] }"
+        >
+          <el-input v-model="listenerForm.delegateExpression" clearable />
+        </el-form-item>
+        <template v-if="listenerForm.listenerType === 'scriptListener'">
+          <el-form-item
+            label="鑴氭湰鏍煎紡"
+            prop="scriptFormat"
+            key="listener-script-format"
+            :rules="{ required: true, trigger: ['blur', 'change'], message: '璇峰~鍐欒剼鏈牸寮�' }"
+          >
+            <el-input v-model="listenerForm.scriptFormat" clearable />
+          </el-form-item>
+          <el-form-item
+            label="鑴氭湰绫诲瀷"
+            prop="scriptType"
+            key="listener-script-type"
+            :rules="{ required: true, trigger: ['blur', 'change'], message: '璇烽�夋嫨鑴氭湰绫诲瀷' }"
+          >
+            <el-select v-model="listenerForm.scriptType">
+              <el-option label="鍐呰仈鑴氭湰" value="inlineScript" />
+              <el-option label="澶栭儴鑴氭湰" value="externalScript" />
+            </el-select>
+          </el-form-item>
+          <el-form-item
+            v-if="listenerForm.scriptType === 'inlineScript'"
+            label="鑴氭湰鍐呭"
+            prop="value"
+            key="listener-script"
+            :rules="{ required: true, trigger: ['blur', 'change'], message: '璇峰~鍐欒剼鏈唴瀹�' }"
+          >
+            <el-input v-model="listenerForm.value" clearable />
+          </el-form-item>
+          <el-form-item
+            v-if="listenerForm.scriptType === 'externalScript'"
+            label="璧勬簮鍦板潃"
+            prop="resource"
+            key="listener-resource"
+            :rules="{ required: true, trigger: ['blur', 'change'], message: '璇峰~鍐欒祫婧愬湴鍧�' }"
+          >
+            <el-input v-model="listenerForm.resource" clearable />
+          </el-form-item>
+        </template>
+      </el-form>
+      <el-divider />
+      <p class="listener-filed__title">
+        <span><el-icon><Menu /></el-icon>娉ㄥ叆瀛楁锛�</span>
+        <el-button size="small" type="primary" @click="openListenerFieldForm(null)">娣诲姞瀛楁</el-button>
+      </p>
+      <el-table :data="fieldsListOfListener" size="small" max-height="240" border fit style="flex: none">
+        <el-table-column label="搴忓彿" width="50px" type="index" />
+        <el-table-column label="瀛楁鍚嶇О" min-width="100px" prop="name" />
+        <el-table-column label="瀛楁绫诲瀷" min-width="80px" show-overflow-tooltip :formatter="row => fieldTypeObject[row.fieldType]" />
+        <el-table-column label="瀛楁鍊�/琛ㄨ揪寮�" min-width="100px" show-overflow-tooltip :formatter="row => row.string || row.expression" />
+        <el-table-column label="鎿嶄綔" width="100px">
+          <template v-slot="{ row, $index }">
+            <el-button link type="" @click="openListenerFieldForm(row, $index)">缂栬緫</el-button>
+            <el-divider direction="vertical" />
+            <el-button link type="" style="color: #ff4d4f" @click="removeListenerField(row, $index)">绉婚櫎</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <div class="element-drawer__button">
+        <el-button size="small" @click="listenerFormModelVisible = false">鍙� 娑�</el-button>
+        <el-button size="small" type="primary" @click="saveListenerConfig">淇� 瀛�</el-button>
+      </div>
+    </el-drawer>
+
+    <!-- 娉ㄥ叆瑗挎 缂栬緫/鍒涘缓 閮ㄥ垎 -->
+    <el-dialog title="瀛楁閰嶇疆" v-model="listenerFieldFormModelVisible" width="600px" append-to-body destroy-on-close>
+      <el-form :model="listenerFieldForm" size="small" label-width="96px" ref="listenerFieldFormRef" style="height: 136px" @submit.prevent>
+        <el-form-item label="瀛楁鍚嶇О锛�" prop="name" :rules="{ required: true, trigger: ['blur', 'change'] }">
+          <el-input v-model="listenerFieldForm.name" clearable />
+        </el-form-item>
+        <el-form-item label="瀛楁绫诲瀷锛�" prop="fieldType" :rules="{ required: true, trigger: ['blur', 'change'] }">
+          <el-select v-model="listenerFieldForm.fieldType">
+            <el-option v-for="i in Object.keys(fieldTypeObject)" :key="i" :label="fieldTypeObject[i]" :value="i" />
+          </el-select>
+        </el-form-item>
+        <el-form-item
+          v-if="listenerFieldForm.fieldType === 'string'"
+          label="瀛楁鍊硷細"
+          prop="string"
+          key="field-string"
+          :rules="{ required: true, trigger: ['blur', 'change'] }"
+        >
+          <el-input v-model="listenerFieldForm.string" clearable />
+        </el-form-item>
+        <el-form-item
+          v-if="listenerFieldForm.fieldType === 'expression'"
+          label="琛ㄨ揪寮忥細"
+          prop="expression"
+          key="field-expression"
+          :rules="{ required: true, trigger: ['blur', 'change'] }"
+        >
+          <el-input v-model="listenerFieldForm.expression" clearable />
+        </el-form-item>
+      </el-form>
+      <template v-slot:footer>
+        <el-button size="small" @click="listenerFieldFormModelVisible = false">鍙� 娑�</el-button>
+        <el-button size="small" type="primary" @click="saveListenerFiled">纭� 瀹�</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+<script>
+import { createListenerObject, updateElementExtensions } from "../../utils";
+import { initListenerType, initListenerForm, LISTENER_TYPE, FIELD_TYPE } from "./utilSelf";
+
+export default {
+  name: "ElementListeners",
+  setup() {
+    return { Plus }
+  },
+  props: {
+    id: String,
+    type: String
+  },
+  inject: {
+    prefix: "prefix",
+    width: "width"
+  },
+  data() {
+    return {
+      elementListenersList: [], // 鐩戝惉鍣ㄥ垪琛�
+      listenerForm: {}, // 鐩戝惉鍣ㄨ鎯呰〃鍗�
+      listenerFormModelVisible: false, // 鐩戝惉鍣� 缂栬緫 渚ц竟鏍忔樉绀虹姸鎬�
+      fieldsListOfListener: [],
+      listenerFieldForm: {}, // 鐩戝惉鍣� 娉ㄥ叆瀛楁 璇︽儏琛ㄥ崟
+      listenerFieldFormModelVisible: false, // 鐩戝惉鍣� 娉ㄥ叆瀛楁琛ㄥ崟寮圭獥 鏄剧ず鐘舵��
+      editingListenerIndex: -1, // 鐩戝惉鍣ㄦ墍鍦ㄤ笅鏍囷紝-1 涓烘柊澧�
+      editingListenerFieldIndex: -1, // 瀛楁鎵�鍦ㄤ笅鏍囷紝-1 涓烘柊澧�
+      listenerTypeObject: LISTENER_TYPE,
+      fieldTypeObject: FIELD_TYPE
+    };
+  },
+  watch: {
+    id: {
+      immediate: true,
+      handler(val) {
+        val && val.length && this.$nextTick(() => this.resetListenersList());
+      }
+    }
+  },
+  methods: {
+    resetListenersList() {
+      this.bpmnElement = window.bpmnInstances.bpmnElement;
+      this.otherExtensionList = [];
+      this.bpmnElementListeners =
+        this.bpmnElement.businessObject?.extensionElements?.values?.filter(ex => ex.$type === `${this.prefix}:ExecutionListener`) ?? [];
+      this.elementListenersList = this.bpmnElementListeners.map(listener => initListenerType(listener));
+    },
+    // 鎵撳紑 鐩戝惉鍣ㄨ鎯� 渚ц竟鏍�
+    openListenerForm(listener, index) {
+      if (listener) {
+        this.listenerForm = initListenerForm(listener);
+        this.editingListenerIndex = index;
+      } else {
+        this.listenerForm = {};
+        this.editingListenerIndex = -1; // 鏍囪涓烘柊澧�
+      }
+      if (listener && listener.fields) {
+        this.fieldsListOfListener = listener.fields.map(field => ({
+          ...field,
+          fieldType: field.string ? "string" : "expression"
+        }));
+      } else {
+        this.fieldsListOfListener = [];
+        this.listenerForm["fields"] = []
+      }
+      // 鎵撳紑渚ц竟鏍忓苟娓呮楠岃瘉鐘舵��
+      this.listenerFormModelVisible = true;
+      this.$nextTick(() => {
+        if (this.$refs["listenerFormRef"]) this.$refs["listenerFormRef"].clearValidate();
+      });
+    },
+    // 鎵撳紑鐩戝惉鍣ㄥ瓧娈电紪杈戝脊绐�
+    openListenerFieldForm(field, index) {
+      this.listenerFieldForm = field ? JSON.parse(JSON.stringify(field)) : {};
+      this.editingListenerFieldIndex = field ? index : -1;
+      this.listenerFieldFormModelVisible = true;
+      this.$nextTick(() => {
+        if (this.$refs["listenerFieldFormRef"]) this.$refs["listenerFieldFormRef"].clearValidate();
+      });
+    },
+    // 淇濆瓨鐩戝惉鍣ㄦ敞鍏ュ瓧娈�
+    async saveListenerFiled() {
+      let validateStatus = await this.$refs["listenerFieldFormRef"].validate();
+      if (!validateStatus) return; // 楠岃瘉涓嶉�氳繃鐩存帴杩斿洖
+      if (this.editingListenerFieldIndex === -1) {
+        this.fieldsListOfListener.push(this.listenerFieldForm);
+        this.listenerForm.fields.push(this.listenerFieldForm);
+      } else {
+        this.fieldsListOfListener.splice(this.editingListenerFieldIndex, 1, this.listenerFieldForm);
+        this.listenerForm.fields.splice(this.editingListenerFieldIndex, 1, this.listenerFieldForm);
+      }
+      this.listenerFieldFormModelVisible = false;
+      this.$nextTick(() => (this.listenerFieldForm = {}));
+    },
+    // 绉婚櫎鐩戝惉鍣ㄥ瓧娈�
+    removeListenerField(field, index) {
+      this.$confirm("纭绉婚櫎璇ュ瓧娈靛悧锛�", "鎻愮ず", {
+        confirmButtonText: "纭� 璁�",
+        cancelButtonText: "鍙� 娑�"
+      })
+        .then(() => {
+          this.fieldsListOfListener.splice(index, 1);
+          this.listenerForm.fields.splice(index, 1);
+        })
+        .catch(() => console.info("鎿嶄綔鍙栨秷"));
+    },
+    // 绉婚櫎鐩戝惉鍣�
+    removeListener(listener, index) {
+      this.$confirm("纭绉婚櫎璇ョ洃鍚櫒鍚楋紵", "鎻愮ず", {
+        confirmButtonText: "纭� 璁�",
+        cancelButtonText: "鍙� 娑�"
+      })
+        .then(() => {
+          this.bpmnElementListeners.splice(index, 1);
+          this.elementListenersList.splice(index, 1);
+          updateElementExtensions(this.bpmnElement, this.otherExtensionList.concat(this.bpmnElementListeners));
+        })
+        .catch(() => console.info("鎿嶄綔鍙栨秷"));
+    },
+    // 淇濆瓨鐩戝惉鍣ㄩ厤缃�
+    async saveListenerConfig() {
+      let validateStatus = await this.$refs["listenerFormRef"].validate();
+      if (!validateStatus) return; // 楠岃瘉涓嶉�氳繃鐩存帴杩斿洖
+      const listenerObject = createListenerObject(this.listenerForm, false, this.prefix);
+      if (this.editingListenerIndex === -1) {
+        this.bpmnElementListeners.push(listenerObject);
+        this.elementListenersList.push(this.listenerForm);
+      } else {
+        this.bpmnElementListeners.splice(this.editingListenerIndex, 1, listenerObject);
+        this.elementListenersList.splice(this.editingListenerIndex, 1, this.listenerForm);
+      }
+      // 淇濆瓨鍏朵粬閰嶇疆
+      this.otherExtensionList = this.bpmnElement.businessObject?.extensionElements?.values?.filter(ex => ex.$type !== `${this.prefix}:ExecutionListener`) ?? [];
+      updateElementExtensions(this.bpmnElement, this.otherExtensionList.concat(this.bpmnElementListeners));
+      // 4. 闅愯棌渚ц竟鏍�
+      this.listenerFormModelVisible = false;
+      this.listenerForm = {};
+    }
+  }
+};
+</script>
diff --git a/ruoyi-ui/apps/web-antd/src/package/penal/listeners/UserTaskListeners.vue b/ruoyi-ui/apps/web-antd/src/package/penal/listeners/UserTaskListeners.vue
new file mode 100644
index 0000000..3644da8
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/penal/listeners/UserTaskListeners.vue
@@ -0,0 +1,335 @@
+<template>
+  <div class="panel-tab__content">
+    <el-table :data="elementListenersList" size="small" border>
+      <el-table-column label="搴忓彿" width="50px" type="index" />
+      <el-table-column label="浜嬩欢绫诲瀷" min-width="80px" show-overflow-tooltip :formatter="formatterEvent" />
+      <!--      <el-table-column label="浜嬩欢id" min-width="80px" prop="id" show-overflow-tooltip />-->
+      <el-table-column label="鐩戝惉鍣ㄧ被鍨�" min-width="80px" show-overflow-tooltip :formatter="formatterListener" />
+      <el-table-column label="鎿嶄綔" width="90px">
+        <template v-slot="{ row, $index }">
+          <el-button link type="primary" @click="openListenerForm(row, $index)">缂栬緫</el-button>
+          <el-button link type="danger" @click="removeListener(row, $index)">绉婚櫎</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <div class="element-drawer__button">
+      <el-button size="small" type="primary" :icon="Plus" @click="openListenerForm(null)">娣诲姞鐩戝惉鍣�</el-button>
+    </div>
+
+    <!-- 鐩戝惉鍣� 缂栬緫/鍒涘缓 閮ㄥ垎 -->
+    <el-drawer
+      v-model="listenerFormModelVisible"
+      title="浠诲姟鐩戝惉鍣�"
+      :size="`${width}px`"
+      class="listener-drawer"
+      append-to-body
+      destroy-on-close>
+      <el-form :model="listenerForm" label-width="96px" ref="listenerFormRef" @submit.prevent>
+        <el-form-item label="浜嬩欢绫诲瀷" prop="event" :rules="{ required: true, trigger: ['blur', 'change'], message: '璇烽�夋嫨浜嬩欢绫诲瀷' }">
+          <el-select v-model="listenerForm.event" style="width: 100%">
+            <el-option v-for="item in TASK_EVENT_TYPE" :key="item.value" :label="item.label" :value="item.value" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="鐩戝惉鍣ㄧ被鍨�" prop="listenerType" :rules="{ required: true, trigger: ['blur', 'change'], message: '璇烽�夋嫨鐩戝惉鍣ㄧ被鍨�' }">
+          <el-input
+            v-model="listenerForm[selectedProp]"
+            clearable
+            class="input-with-select">
+            <template #prepend>
+              <el-select v-model="listenerForm.listenerType" style="width: 100%">
+                <el-option v-for="item in LISTENER_TYPE" :key="item.key" :label="item.label" :value="item.value" />
+              </el-select>
+            </template>
+            <template #append>
+              <el-button :icon="Search" />
+            </template>
+          </el-input>
+
+        </el-form-item>
+        <!--   鑴氭湰绫诲瀷       -->
+        <!--        <template v-if="listenerForm.listenerType === LISTENER_TYPE[LISTENER_TYPE.length - 1].value">-->
+        <!--          <el-form-item-->
+        <!--            label="鑴氭湰鏍煎紡"-->
+        <!--            prop="scriptFormat"-->
+        <!--            key="listener-script-format"-->
+        <!--            :rules="{ required: true, trigger: ['blur', 'change'], message: '璇峰~鍐欒剼鏈牸寮�' }"-->
+        <!--          >-->
+        <!--            <el-input v-model="listenerForm.scriptFormat" clearable />-->
+        <!--          </el-form-item>-->
+        <!--          <el-form-item-->
+        <!--            label="鑴氭湰绫诲瀷"-->
+        <!--            prop="scriptType"-->
+        <!--            key="listener-script-type"-->
+        <!--            :rules="{ required: true, trigger: ['blur', 'change'], message: '璇烽�夋嫨鑴氭湰绫诲瀷' }"-->
+        <!--          >-->
+        <!--            <el-select v-model="listenerForm.scriptType">-->
+        <!--              <el-option v-for="item in SCRIPT_TYPE" :key="item.value" :label="item.label" :value="item.value" />-->
+        <!--            </el-select>-->
+        <!--          </el-form-item>-->
+        <!--          <el-form-item-->
+        <!--            v-if="listenerForm.scriptType === SCRIPT_TYPE[0].value"-->
+        <!--            label="鑴氭湰鍐呭"-->
+        <!--            prop="value"-->
+        <!--            key="listener-script"-->
+        <!--            :rules="{ required: true, trigger: ['blur', 'change'], message: '璇峰~鍐欒剼鏈唴瀹�' }"-->
+        <!--          >-->
+        <!--            <el-input v-model="listenerForm.value" clearable />-->
+        <!--          </el-form-item>-->
+        <!--          <el-form-item-->
+        <!--            v-if="listenerForm.scriptType === SCRIPT_TYPE[1].value"-->
+        <!--            label="璧勬簮鍦板潃"-->
+        <!--            prop="resource"-->
+        <!--            key="listener-resource"-->
+        <!--            :rules="{ required: true, trigger: ['blur', 'change'], message: '璇峰~鍐欒祫婧愬湴鍧�' }"-->
+        <!--          >-->
+        <!--            <el-input v-model="listenerForm.resource" clearable />-->
+        <!--          </el-form-item>-->
+        <!--        </template>-->
+        <!--  鐩戝惉浜嬩欢锛� 瓒呮椂      -->
+        <template v-if="listenerForm.event === TASK_EVENT_TYPE[TASK_EVENT_TYPE.length - 1].value">
+          <el-form-item label="瀹氭椂鍣ㄧ被鍨�" prop="eventDefinitionType" key="eventDefinitionType">
+            <el-select v-model="listenerForm.eventDefinitionType">
+              <el-option v-for="item in EVENT_DEFINITION_TYPE" :key="item.value" :value="item.value" :label="item.label" />
+            </el-select>
+          </el-form-item>
+          <el-form-item
+            v-if="!!listenerForm.eventDefinitionType && listenerForm.eventDefinitionType !== 'null'"
+            label="瀹氭椂鍣�"
+            prop="eventTimeDefinitions"
+            key="eventTimeDefinitions"
+            :rules="{ required: true, trigger: ['blur', 'change'], message: '璇峰~鍐欏畾鏃跺櫒閰嶇疆' }"
+          >
+            <el-input v-model="listenerForm.eventTimeDefinitions" clearable />
+          </el-form-item>
+        </template>
+      </el-form>
+      <el-divider />
+      <div class="listener-filed__title">
+        <span><el-icon><Menu /></el-icon>娉ㄥ叆瀛楁锛�</span>
+        <el-button size="small" type="primary" @click="openListenerFieldForm(null)">娣诲姞瀛楁</el-button>
+      </div>
+      <el-table :data="fieldsListOfListener" size="small" max-height="240" border fit style="flex: none">
+        <el-table-column label="搴忓彿" width="50px" type="index" />
+        <el-table-column label="瀛楁鍚嶇О" min-width="100px" prop="name" />
+        <el-table-column label="瀛楁绫诲瀷" min-width="80px" show-overflow-tooltip :formatter="formatterField" />
+        <el-table-column label="瀛楁鍊�/琛ㄨ揪寮�" min-width="100px" show-overflow-tooltip :formatter="row => row.string || row.expression" />
+        <el-table-column label="鎿嶄綔" width="100px">
+          <template v-slot="{ row, $index }">
+            <el-button link type="primary" @click="openListenerFieldForm(row, $index)">缂栬緫</el-button>
+            <el-button link type="danger" @click="removeListenerField(row, $index)">绉婚櫎</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <div class="element-drawer__button">
+        <el-button @click="listenerFormModelVisible = false">鍙� 娑�</el-button>
+        <el-button type="primary" @click="saveListenerConfig">淇� 瀛�</el-button>
+      </div>
+    </el-drawer>
+
+    <!-- 娉ㄥ叆瑗挎 缂栬緫/鍒涘缓 閮ㄥ垎 -->
+    <el-dialog title="瀛楁閰嶇疆" v-model="listenerFieldFormModelVisible" width="600px" append-to-body destroy-on-close>
+      <el-form :model="listenerFieldForm" size="small" label-width="96px" ref="listenerFieldFormRef" style="height: 136px" @submit.prevent>
+        <el-form-item label="瀛楁鍚嶇О锛�" prop="name" :rules="{ required: true, trigger: ['blur', 'change'], message: '璇峰~鍐欏瓧娈靛悕绉�' }">
+          <el-input v-model="listenerFieldForm.name" clearable />
+        </el-form-item>
+        <el-form-item label="瀛楁绫诲瀷锛�" prop="fieldType" :rules="{ required: true, trigger: ['blur', 'change'], message: '璇烽�夋嫨瀛楁绫诲瀷' }">
+          <el-select v-model="listenerFieldForm.fieldType">
+            <el-option v-for="item in FIELD_TYPE" :key="item.value" :label="item.label" :value="item.value" />
+          </el-select>
+        </el-form-item>
+        <el-form-item
+          v-if="listenerFieldForm.fieldType === FIELD_TYPE[0].value"
+          label="瀛楁鍊硷細"
+          prop="string"
+          key="field-string"
+          :rules="{ required: true, trigger: ['blur', 'change'], message: '璇峰~鍐欏瓧娈靛��' }"
+        >
+          <el-input v-model="listenerFieldForm.string" clearable />
+        </el-form-item>
+        <el-form-item
+          v-if="listenerFieldForm.fieldType === FIELD_TYPE[1].value"
+          label="琛ㄨ揪寮忥細"
+          prop="expression"
+          key="field-expression"
+          :rules="{ required: true, trigger: ['blur', 'change'], message: '璇峰~鍐欒〃杈惧紡' }"
+        >
+          <el-input v-model="listenerFieldForm.expression" clearable />
+        </el-form-item>
+      </el-form>
+      <template v-slot:footer>
+        <el-button @click="listenerFieldFormModelVisible = false">鍙� 娑�</el-button>
+        <el-button type="primary" @click="saveListenerFiled">纭� 瀹�</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+<script>
+import { createListenerObject, updateElementExtensions } from "../../utils";
+import {
+  initListenerForm,
+  initListenerType,
+  FIELD_TYPE,
+  LISTENER_TYPE,
+  TASK_EVENT_TYPE,
+  SCRIPT_TYPE,
+  EVENT_DEFINITION_TYPE
+} from "./utilSelf";
+export default {
+  name: "UserTaskListeners",
+  props: {
+    id: String,
+    type: String
+  },
+  inject: {
+    prefix: "prefix",
+    width: "width"
+  },
+  setup() {
+    return { Plus, Search }
+  },
+  data() {
+    return {
+      elementListenersList: [],
+      listenerFormModelVisible: false,
+      listenerForm: {},
+      fieldsListOfListener: [],
+      listenerFieldFormModelVisible: false, // 鐩戝惉鍣� 娉ㄥ叆瀛楁琛ㄥ崟寮圭獥 鏄剧ず鐘舵��
+      editingListenerIndex: -1, // 鐩戝惉鍣ㄦ墍鍦ㄤ笅鏍囷紝-1 涓烘柊澧�
+      editingListenerFieldIndex: -1, // 瀛楁鎵�鍦ㄤ笅鏍囷紝-1 涓烘柊澧�
+      listenerFieldForm: {} // 鐩戝惉鍣� 娉ㄥ叆瀛楁 璇︽儏琛ㄥ崟
+    };
+  },
+  watch: {
+    id: {
+      immediate: true,
+      handler(val) {
+        val && val.length && this.$nextTick(() => this.resetListenersList());
+      }
+    }
+  },
+  computed: {
+    TASK_EVENT_TYPE() { return TASK_EVENT_TYPE },
+    LISTENER_TYPE() { return LISTENER_TYPE },
+    FIELD_TYPE() { return FIELD_TYPE },
+    SCRIPT_TYPE() { return SCRIPT_TYPE },
+    EVENT_DEFINITION_TYPE() { return EVENT_DEFINITION_TYPE },
+    formatterEvent() {
+      return (row) => {
+        return TASK_EVENT_TYPE.find(find => find.value === row.event)?.label || ""
+      }
+    },
+    formatterListener() {
+      return (row) => {
+        return LISTENER_TYPE.find(find => find.value === row.listener)?.label || ""
+      }
+    },
+    formatterField() {
+      return (row) => {
+        return FIELD_TYPE.find(find => find.value === row.fieldType)?.label || ""
+      }
+    },
+    selectedProp() {
+      return LISTENER_TYPE.find(find => find.value === this.listenerForm.listenerType)?.prop || ''
+    }
+  },
+  methods: {
+    resetListenersList() {
+      this.bpmnElement = window.bpmnInstances.bpmnElement;
+      this.otherExtensionList = [];
+      this.bpmnElementListeners = this.bpmnElement.businessObject?.extensionElements?.values?.filter(ex => ex.$type === `${this.prefix}:TaskListener`) ?? [];
+      this.elementListenersList = this.bpmnElementListeners.map(listener => initListenerType(listener));
+    },
+    openListenerForm(listener, index) {
+      if (listener) {
+        this.listenerForm = initListenerForm(listener);
+        this.editingListenerIndex = index;
+      } else {
+        this.listenerForm = {};
+        this.editingListenerIndex = -1; // 鏍囪涓烘柊澧�
+      }
+      if (listener && listener.fields) {
+        this.fieldsListOfListener = listener.fields.map(field => ({
+          ...field,
+          fieldType: field.string ? "string" : "expression"
+        }));
+      } else {
+        this.fieldsListOfListener = [];
+        this.listenerForm["fields"] = []
+      }
+      // 鎵撳紑渚ц竟鏍忓苟娓呮楠岃瘉鐘舵��
+      this.listenerFormModelVisible = true;
+      this.$nextTick(() => {
+        if (this.$refs["listenerFormRef"]) this.$refs["listenerFormRef"].clearValidate();
+      });
+    },
+    // 绉婚櫎鐩戝惉鍣�
+    removeListener(listener, index) {
+      this.$confirm("纭绉婚櫎璇ョ洃鍚櫒鍚楋紵", "鎻愮ず", {
+        confirmButtonText: "纭� 璁�",
+        cancelButtonText: "鍙� 娑�"
+      })
+        .then(() => {
+          this.bpmnElementListeners.splice(index, 1);
+          this.elementListenersList.splice(index, 1);
+          updateElementExtensions(this.bpmnElement, this.otherExtensionList.concat(this.bpmnElementListeners));
+        })
+        .catch(() => console.info("鎿嶄綔鍙栨秷"));
+    },
+    // 淇濆瓨鐩戝惉鍣�
+    async saveListenerConfig() {
+      let validateStatus = await this.$refs["listenerFormRef"].validate();
+      if (!validateStatus) return; // 楠岃瘉涓嶉�氳繃鐩存帴杩斿洖
+      const listenerObject = createListenerObject(this.listenerForm, true, this.prefix);
+      if (this.editingListenerIndex === -1) {
+        this.bpmnElementListeners.push(listenerObject);
+        this.elementListenersList.push(this.listenerForm);
+      } else {
+        this.bpmnElementListeners.splice(this.editingListenerIndex, 1, listenerObject);
+        this.elementListenersList.splice(this.editingListenerIndex, 1, this.listenerForm);
+      }
+      // 淇濆瓨鍏朵粬閰嶇疆
+      this.otherExtensionList = this.bpmnElement.businessObject?.extensionElements?.values?.filter(ex => ex.$type !== `${this.prefix}:TaskListener`) ?? [];
+      updateElementExtensions(this.bpmnElement, this.otherExtensionList.concat(this.bpmnElementListeners));
+      // 4. 闅愯棌渚ц竟鏍�
+      this.listenerFormModelVisible = false;
+      this.listenerForm = {};
+    },
+    // 鎵撳紑鐩戝惉鍣ㄥ瓧娈电紪杈戝脊绐�
+    openListenerFieldForm(field, index) {
+      this.listenerFieldForm = field ? JSON.parse(JSON.stringify(field)) : {};
+      this.editingListenerFieldIndex = field ? index : -1;
+      this.listenerFieldFormModelVisible = true;
+      this.$nextTick(() => {
+        if (this.$refs["listenerFieldFormRef"]) this.$refs["listenerFieldFormRef"].clearValidate();
+      });
+    },
+    // 淇濆瓨鐩戝惉鍣ㄦ敞鍏ュ瓧娈�
+    async saveListenerFiled() {
+      let validateStatus = await this.$refs["listenerFieldFormRef"].validate();
+      if (!validateStatus) return; // 楠岃瘉涓嶉�氳繃鐩存帴杩斿洖
+      if (this.editingListenerFieldIndex === -1) {
+        this.fieldsListOfListener.push(this.listenerFieldForm);
+        this.listenerForm.fields.push(this.listenerFieldForm);
+      } else {
+        this.fieldsListOfListener.splice(this.editingListenerFieldIndex, 1, this.listenerFieldForm);
+        this.listenerForm.fields.splice(this.editingListenerFieldIndex, 1, this.listenerFieldForm);
+      }
+      this.listenerFieldFormModelVisible = false;
+      this.$nextTick(() => (this.listenerFieldForm = {}));
+    },
+    // 绉婚櫎鐩戝惉鍣ㄥ瓧娈�
+    removeListenerField(field, index) {
+      this.$confirm("纭绉婚櫎璇ュ瓧娈靛悧锛�", "鎻愮ず", {
+        confirmButtonText: "纭� 璁�",
+        cancelButtonText: "鍙� 娑�"
+      })
+        .then(() => {
+          this.fieldsListOfListener.splice(index, 1);
+          this.listenerForm.fields.splice(index, 1);
+        })
+        .catch(() => console.info("鎿嶄綔鍙栨秷"));
+    }
+  }
+};
+</script>
diff --git a/ruoyi-ui/apps/web-antd/src/package/penal/listeners/template.js b/ruoyi-ui/apps/web-antd/src/package/penal/listeners/template.js
new file mode 100644
index 0000000..2839c1f
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/penal/listeners/template.js
@@ -0,0 +1,178 @@
+export const template = isTaskListener => {
+  return `
+  <div class="panel-tab__content">
+    <el-table :data="elementListenersList" size="small" border>
+      <el-table-column label="搴忓彿" width="50px" type="index" />
+      <el-table-column label="浜嬩欢绫诲瀷" min-width="100px" prop="event" />
+      <el-table-column label="鐩戝惉鍣ㄧ被鍨�" min-width="100px" show-overflow-tooltip :formatter="row => listenerTypeObject[row.listenerType]" />
+      <el-table-column label="鎿嶄綔" width="90px">
+        <template v-slot="{ row, $index }">
+          <el-button link type="" @click="openListenerForm(row, $index)">缂栬緫</el-button>
+          <el-divider direction="vertical" />
+          <el-button link type="" style="color: #ff4d4f" @click="removeListener(row, $index)">绉婚櫎</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <div class="element-drawer__button">
+      <el-button size="small" type="primary" :icon="Plus" @click="openListenerForm(null)">娣诲姞鐩戝惉鍣�</el-button>
+    </div>
+
+    <!-- 鐩戝惉鍣� 缂栬緫/鍒涘缓 閮ㄥ垎 -->
+    <el-drawer v-model="listenerFormModelVisible" title="鎵ц鐩戝惉鍣�" :size="width + 'px'" append-to-body destroy-on-close>
+      <el-form size="small" :model="listenerForm" label-width="96px" ref="listenerFormRef" @submit.prevent>
+        <el-form-item label="浜嬩欢绫诲瀷" prop="event" :rules="{ required: true, trigger: ['blur', 'change'] }">
+          <el-select v-model="listenerForm.event">
+            <el-option label="start" value="start" />
+            <el-option label="end" value="end" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="鐩戝惉鍣ㄧ被鍨�" prop="listenerType" :rules="{ required: true, trigger: ['blur', 'change'] }">
+          <el-select v-model="listenerForm.listenerType">
+            <el-option v-for="i in Object.keys(listenerTypeObject)" :key="i" :label="listenerTypeObject[i]" :value="i" />
+          </el-select>
+        </el-form-item>
+        <el-form-item
+          v-if="listenerForm.listenerType === 'classListener'"
+          label="Java绫�"
+          prop="class"
+          key="listener-class"
+          :rules="{ required: true, trigger: ['blur', 'change'] }"
+        >
+          <el-input v-model="listenerForm.class" clearable />
+        </el-form-item>
+        <el-form-item
+          v-if="listenerForm.listenerType === 'expressionListener'"
+          label="琛ㄨ揪寮�"
+          prop="expression"
+          key="listener-expression"
+          :rules="{ required: true, trigger: ['blur', 'change'] }"
+        >
+          <el-input v-model="listenerForm.expression" clearable />
+        </el-form-item>
+        <el-form-item
+          v-if="listenerForm.listenerType === 'delegateExpressionListener'"
+          label="浠g悊琛ㄨ揪寮�"
+          prop="delegateExpression"
+          key="listener-delegate"
+          :rules="{ required: true, trigger: ['blur', 'change'] }"
+        >
+          <el-input v-model="listenerForm.delegateExpression" clearable />
+        </el-form-item>
+        <template v-if="listenerForm.listenerType === 'scriptListener'">
+          <el-form-item
+            label="鑴氭湰鏍煎紡"
+            prop="scriptFormat"
+            key="listener-script-format"
+            :rules="{ required: true, trigger: ['blur', 'change'], message: '璇峰~鍐欒剼鏈牸寮�' }"
+          >
+            <el-input v-model="listenerForm.scriptFormat" clearable />
+          </el-form-item>
+          <el-form-item
+            label="鑴氭湰绫诲瀷"
+            prop="scriptType"
+            key="listener-script-type"
+            :rules="{ required: true, trigger: ['blur', 'change'], message: '璇烽�夋嫨鑴氭湰绫诲瀷' }"
+          >
+            <el-select v-model="listenerForm.scriptType">
+              <el-option label="鍐呰仈鑴氭湰" value="inlineScript" />
+              <el-option label="澶栭儴鑴氭湰" value="externalScript" />
+            </el-select>
+          </el-form-item>
+          <el-form-item
+            v-if="listenerForm.scriptType === 'inlineScript'"
+            label="鑴氭湰鍐呭"
+            prop="value"
+            key="listener-script"
+            :rules="{ required: true, trigger: ['blur', 'change'], message: '璇峰~鍐欒剼鏈唴瀹�' }"
+          >
+            <el-input v-model="listenerForm.value" clearable />
+          </el-form-item>
+          <el-form-item
+            v-if="listenerForm.scriptType === 'externalScript'"
+            label="璧勬簮鍦板潃"
+            prop="resource"
+            key="listener-resource"
+            :rules="{ required: true, trigger: ['blur', 'change'], message: '璇峰~鍐欒祫婧愬湴鍧�' }"
+          >
+            <el-input v-model="listenerForm.resource" clearable />
+          </el-form-item>
+        </template>
+        ${
+  isTaskListener
+    ? "<el-form-item label='瀹氭椂鍣ㄧ被鍨�' prop='eventDefinitionType' key='eventDefinitionType'>" +
+              "<el-select v-model='listenerForm.eventDefinitionType'>" +
+              "<el-option label='鏃ユ湡' value='date' />" +
+              "<el-option label='鎸佺画鏃堕暱' value='duration' />" +
+              "<el-option label='寰幆' value='cycle' />" +
+              "<el-option label='鏃�' value='' />" +
+              '</el-select>' +
+              '</el-form-item>' +
+              "<el-form-item v-if='!!listenerForm.eventDefinitionType' label='瀹氭椂鍣�' prop='eventDefinitions' key='eventDefinitions'>" +
+              "<el-input v-model='listenerForm.eventDefinitions' clearable />" +
+              '</el-form-item>'
+    : ''
+}
+      </el-form>
+      <el-divider />
+      <p class="listener-filed__title">
+        <span><el-icon><Menu /></el-icon>娉ㄥ叆瀛楁锛�</span>
+        <el-button size="small" type="primary" @click="openListenerFieldForm(null)">娣诲姞瀛楁</el-button>
+      </p>
+      <el-table :data="fieldsListOfListener" size="small" max-height="240" border fit style="flex: none">
+        <el-table-column label="搴忓彿" width="50px" type="index" />
+        <el-table-column label="瀛楁鍚嶇О" min-width="100px" prop="name" />
+        <el-table-column label="瀛楁绫诲瀷" min-width="80px" show-overflow-tooltip :formatter="row => fieldTypeObject[row.fieldType]" />
+        <el-table-column label="瀛楁鍊�/琛ㄨ揪寮�" min-width="100px" show-overflow-tooltip :formatter="row => row.string || row.expression" />
+        <el-table-column label="鎿嶄綔" width="100px">
+          <template v-slot="{ row, $index }">
+            <el-button link type="" @click="openListenerFieldForm(row, $index)">缂栬緫</el-button>
+            <el-divider direction="vertical" />
+            <el-button link type="" style="color: #ff4d4f" @click="removeListenerField(row, $index)">绉婚櫎</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <div class="element-drawer__button">
+        <el-button size="small" @click="listenerFormModelVisible = false">鍙� 娑�</el-button>
+        <el-button size="small" type="primary" @click="saveListenerConfig">淇� 瀛�</el-button>
+      </div>
+    </el-drawer>
+
+    <!-- 娉ㄥ叆瑗挎 缂栬緫/鍒涘缓 閮ㄥ垎 -->
+    <el-dialog title="瀛楁閰嶇疆" v-model="listenerFieldFormModelVisible" width="600px" append-to-body destroy-on-close>
+      <el-form :model="listenerFieldForm" size="small" label-width="96px" ref="listenerFieldFormRef" style="height: 136px" @submit.prevent>
+        <el-form-item label="瀛楁鍚嶇О锛�" prop="name" :rules="{ required: true, trigger: ['blur', 'change'] }">
+          <el-input v-model="listenerFieldForm.name" clearable />
+        </el-form-item>
+        <el-form-item label="瀛楁绫诲瀷锛�" prop="fieldType" :rules="{ required: true, trigger: ['blur', 'change'] }">
+          <el-select v-model="listenerFieldForm.fieldType">
+            <el-option v-for="i in Object.keys(fieldTypeObject)" :key="i" :label="fieldTypeObject[i]" :value="i" />
+          </el-select>
+        </el-form-item>
+        <el-form-item
+          v-if="listenerFieldForm.fieldType === 'string'"
+          label="瀛楁鍊硷細"
+          prop="string"
+          key="field-string"
+          :rules="{ required: true, trigger: ['blur', 'change'] }"
+        >
+          <el-input v-model="listenerFieldForm.string" clearable />
+        </el-form-item>
+        <el-form-item
+          v-if="listenerFieldForm.fieldType === 'expression'"
+          label="琛ㄨ揪寮忥細"
+          prop="expression"
+          key="field-expression"
+          :rules="{ required: true, trigger: ['blur', 'change'] }"
+        >
+          <el-input v-model="listenerFieldForm.expression" clearable />
+        </el-form-item>
+      </el-form>
+      <template v-slot:footer>
+        <el-button size="small" @click="listenerFieldFormModelVisible = false">鍙� 娑�</el-button>
+        <el-button size="small" type="primary" @click="saveListenerFiled">纭� 瀹�</el-button>
+      </template>
+    </el-dialog>
+  </div>
+  `;
+};
diff --git a/ruoyi-ui/apps/web-antd/src/package/penal/listeners/utilSelf.js b/ruoyi-ui/apps/web-antd/src/package/penal/listeners/utilSelf.js
new file mode 100644
index 0000000..a90de97
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/penal/listeners/utilSelf.js
@@ -0,0 +1,81 @@
+// 鍒濆鍖栬〃鍗曟暟鎹�
+export function initListenerForm(listener) {
+  let self = {
+    ...listener
+  };
+  if (listener.script) {
+    self = {
+      ...listener,
+      ...listener.script,
+      scriptType: listener.script.resource ? "externalScript" : "inlineScript"
+    };
+  }
+  if (listener.event === "timeout" && listener.eventDefinitions) {
+    if (listener.eventDefinitions.length) {
+      let k = "";
+      for (let key in listener.eventDefinitions[0]) {
+        console.log(listener.eventDefinitions, key);
+        if (key.indexOf("time") !== -1) {
+          k = key;
+          self.eventDefinitionType = key.replace("time", "").toLowerCase();
+        }
+      }
+      console.log(k);
+      self.eventTimeDefinitions = listener.eventDefinitions[0][k].body;
+    }
+  }
+  return self;
+}
+
+export function initListenerType(listener) {
+  let listenerType;
+  if (listener.class) listenerType = "classListener";
+  if (listener.expression) listenerType = "expressionListener";
+  if (listener.delegateExpression) listenerType = "delegateExpressionListener";
+  if (listener.script) listenerType = "scriptListener";
+  return {
+    ...JSON.parse(JSON.stringify(listener)),
+    ...(listener.script ?? {}),
+    listenerType: listenerType
+  };
+}
+
+// 鐩戝惉绫诲瀷
+export const LISTENER_TYPE = [
+  { label: "Java 绫�", value: "classListener", prop: "class", key: "listener-class" },
+  { label: "琛ㄨ揪寮�", value: "expressionListener", prop: "expression", key: "listener-expression" },
+  { label: "浠g悊琛ㄨ揪寮�", value: "delegateExpressionListener", prop: "delegateExpression", key: "listener-delegate" },
+  // { label: "鑴氭湰", value: "scriptListener", prop: "scriptFormat", key: "listener-script-format" },
+]
+// 鑴氭湰绫诲瀷
+export const SCRIPT_TYPE = [
+  { label: "鍐呰仈鑴氭湰", value: "inlineScript" },
+  { label: "澶栭儴鑴氭湰", value: "externalScript" },
+]
+// 浠诲姟鐩戝惉鍣�: 浜嬩欢绫诲瀷
+export const TASK_EVENT_TYPE = [
+  { label: "鍒涘缓", value: "create" },
+  { label: "鎸囨淳", value: "assignment" },
+  { label: "瀹屾垚", value: "complete" },
+  { label: "鍒犻櫎", value: "delete" },
+  { label: "鏇存柊", value: "update" },
+  { label: "瓒呮椂", value: "timeout" },
+]
+// 鎵ц鐩戝惉鍣�: 浜嬩欢绫诲瀷
+export const EXECUTION_EVENT_TYPE = [
+  { label: "寮�濮�", value: "start" },
+  { label: "缁撴潫", value: "end" },
+]
+// 浜嬩欢绫诲瀷: 瀹氭椂鍣ㄧ被鍨�
+export const EVENT_DEFINITION_TYPE = [
+  { label: "鏃�", value: "null" },
+  { label: "鏃ユ湡", value: "date" },
+  { label: "鎸佺画鏃堕暱", value: "duration" },
+  { label: "寰幆", value: "cycle" },
+]
+// 瀛楁閰嶇疆
+export const FIELD_TYPE = [
+  { label: "瀛楃涓�", value: "string" },
+  { label: "琛ㄨ揪寮�", value: "expression" },
+]
+
diff --git a/ruoyi-ui/apps/web-antd/src/package/penal/multi-instance/ElementMultiInstance.vue b/ruoyi-ui/apps/web-antd/src/package/penal/multi-instance/ElementMultiInstance.vue
new file mode 100644
index 0000000..e13eaf5
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/penal/multi-instance/ElementMultiInstance.vue
@@ -0,0 +1,331 @@
+<template>
+  <div class="panel-tab__content">
+    <el-table :data="elementListenersList" size="small" border>
+      <el-table-column label="搴忓彿" width="50px" type="index" />
+      <el-table-column label="浜嬩欢绫诲瀷" min-width="100px" prop="event" :formatter="formatterEvent" />
+      <el-table-column label="鐩戝惉鍣ㄧ被鍨�" min-width="100px" show-overflow-tooltip :formatter="formatterListener" />
+      <el-table-column label="鎿嶄綔" width="90px">
+        <template v-slot="{ row, $index }">
+          <el-button link type="primary" @click="openListenerForm(row, $index)">缂栬緫</el-button>
+          <el-button link type="danger" @click="removeListener(row, $index)">绉婚櫎</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <div class="element-drawer__button">
+      <el-button size="small" type="primary" :icon="Plus" @click="openListenerForm(null)">娣诲姞鐩戝惉鍣�</el-button>
+    </div>
+
+    <!-- 鐩戝惉鍣� 缂栬緫/鍒涘缓 閮ㄥ垎 -->
+    <el-drawer
+      v-model="listenerFormModelVisible"
+      title="鎵ц鐩戝惉鍣�"
+      :size="`${width}px`"
+      class="listener-drawer"
+      append-to-body
+      destroy-on-close>
+      <el-form :model="listenerForm" label-width="96px" ref="listenerFormRef" @submit.prevent>
+        <el-form-item label="浜嬩欢绫诲瀷" prop="event" :rules="{ required: true, trigger: ['blur', 'change'], message: '璇烽�夋嫨浜嬩欢绫诲瀷' }">
+          <el-select v-model="listenerForm.event">
+            <el-option v-for="item in EXECUTION_EVENT_TYPE" :key="item.value" :label="item.label" :value="item.value" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="鐩戝惉鍣ㄧ被鍨�" prop="listenerType" :rules="{ required: true, trigger: ['blur', 'change'], message: '璇烽�夋嫨鐩戝惉鍣ㄧ被鍨�' }">
+          <el-input
+            v-model="listenerForm[selectedProp]"
+            clearable
+            class="input-with-select">
+            <template #prepend>
+              <el-select v-model="listenerForm.listenerType" style="width: 100%">
+                <el-option v-for="item in LISTENER_TYPE" :key="item.key" :label="item.label" :value="item.value" />
+              </el-select>
+            </template>
+            <template #append>
+              <el-button :icon="Search" />
+            </template>
+          </el-input>
+        </el-form-item>
+        <template v-for="item in LISTENER_TYPEWithoutJB">
+          <el-form-item
+            v-if="listenerForm.listenerType === item.value"
+            :key="item.key"
+            :label="item.label"
+            :prop="item.prop"
+            :rules="{ required: true, trigger: ['blur', 'change'] }"
+          >
+            <el-input v-model="listenerForm[item.prop]" clearable />
+          </el-form-item>
+        </template>
+        <!--   鑴氭湰绫诲瀷       -->
+        <!--        <template v-if="listenerForm.listenerType === LISTENER_TYPE[LISTENER_TYPE.length - 1].value">-->
+        <!--          <el-form-item-->
+        <!--            label="鑴氭湰鏍煎紡"-->
+        <!--            prop="scriptFormat"-->
+        <!--            key="listener-script-format"-->
+        <!--            :rules="{ required: true, trigger: ['blur', 'change'], message: '璇峰~鍐欒剼鏈牸寮�' }"-->
+        <!--          >-->
+        <!--            <el-input v-model="listenerForm.scriptFormat" clearable />-->
+        <!--          </el-form-item>-->
+        <!--          <el-form-item-->
+        <!--            label="鑴氭湰绫诲瀷"-->
+        <!--            prop="scriptType"-->
+        <!--            key="listener-script-type"-->
+        <!--            :rules="{ required: true, trigger: ['blur', 'change'], message: '璇烽�夋嫨鑴氭湰绫诲瀷' }"-->
+        <!--          >-->
+        <!--            <el-select v-model="listenerForm.scriptType">-->
+        <!--              <el-option v-for="item in SCRIPT_TYPE" :key="item.value" :label="item.label" :value="item.value" />-->
+        <!--            </el-select>-->
+        <!--          </el-form-item>-->
+        <!--          <el-form-item-->
+        <!--            v-if="listenerForm.scriptType === SCRIPT_TYPE[0].value"-->
+        <!--            label="鑴氭湰鍐呭"-->
+        <!--            prop="value"-->
+        <!--            key="listener-script"-->
+        <!--            :rules="{ required: true, trigger: ['blur', 'change'], message: '璇峰~鍐欒剼鏈唴瀹�' }"-->
+        <!--          >-->
+        <!--            <el-input v-model="listenerForm.value" clearable />-->
+        <!--          </el-form-item>-->
+        <!--          <el-form-item-->
+        <!--            v-if="listenerForm.scriptType === SCRIPT_TYPE[1].value"-->
+        <!--            label="璧勬簮鍦板潃"-->
+        <!--            prop="resource"-->
+        <!--            key="listener-resource"-->
+        <!--            :rules="{ required: true, trigger: ['blur', 'change'], message: '璇峰~鍐欒祫婧愬湴鍧�' }"-->
+        <!--          >-->
+        <!--            <el-input v-model="listenerForm.resource" clearable />-->
+        <!--          </el-form-item>-->
+        <!--        </template>-->
+      </el-form>
+      <el-divider />
+      <div class="listener-filed__title">
+        <span><el-icon><Menu /></el-icon>娉ㄥ叆瀛楁锛�</span>
+        <el-button size="small" type="primary" @click="openListenerFieldForm(null)">娣诲姞瀛楁</el-button>
+      </div>
+      <el-table :data="fieldsListOfListener" size="small" max-height="240" border fit style="flex: none">
+        <el-table-column label="搴忓彿" width="50px" type="index" />
+        <el-table-column label="瀛楁鍚嶇О" min-width="100px" prop="name" />
+        <el-table-column label="瀛楁绫诲瀷" min-width="80px" show-overflow-tooltip :formatter="formatterField" />
+        <el-table-column label="瀛楁鍊�/琛ㄨ揪寮�" min-width="100px" show-overflow-tooltip :formatter="row => row.string || row.expression" />
+        <el-table-column label="鎿嶄綔" width="100px">
+          <template v-slot="{ row, $index }">
+            <el-button link type="primary" @click="openListenerFieldForm(row, $index)">缂栬緫</el-button>
+            <el-button link type="danger" @click="removeListenerField(row, $index)">绉婚櫎</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <div class="element-drawer__button">
+        <el-button size="small" @click="listenerFormModelVisible = false">鍙� 娑�</el-button>
+        <el-button size="small" type="primary" @click="saveListenerConfig">淇� 瀛�</el-button>
+      </div>
+    </el-drawer>
+
+    <!-- 娉ㄥ叆瑗挎 缂栬緫/鍒涘缓 閮ㄥ垎 -->
+    <el-dialog title="瀛楁閰嶇疆" v-model="listenerFieldFormModelVisible" width="600px" append-to-body destroy-on-close>
+      <el-form :model="listenerFieldForm" size="small" label-width="96px" ref="listenerFieldFormRef" style="height: 136px" @submit.prevent>
+        <el-form-item label="瀛楁鍚嶇О锛�" prop="name" :rules="{ required: true, trigger: ['blur', 'change'], message: '璇峰~鍐欏瓧娈靛悕绉�' }">
+          <el-input v-model="listenerFieldForm.name" clearable />
+        </el-form-item>
+        <el-form-item label="瀛楁绫诲瀷锛�" prop="fieldType" :rules="{ required: true, trigger: ['blur', 'change'], message: '璇烽�夋嫨瀛楁绫诲瀷' }">
+          <el-select v-model="listenerFieldForm.fieldType">
+            <el-option v-for="item in FIELD_TYPE" :key="item.value" :label="item.label" :value="item.value" />
+          </el-select>
+        </el-form-item>
+        <el-form-item
+          v-if="listenerFieldForm.fieldType === FIELD_TYPE[0].value"
+          label="瀛楁鍊硷細"
+          prop="string"
+          key="field-string"
+          :rules="{ required: true, trigger: ['blur', 'change'], message: '璇峰~鍐欏瓧娈靛��' }"
+        >
+          <el-input v-model="listenerFieldForm.string" clearable />
+        </el-form-item>
+        <el-form-item
+          v-if="listenerFieldForm.fieldType === FIELD_TYPE[1].value"
+          label="琛ㄨ揪寮忥細"
+          prop="expression"
+          key="field-expression"
+          :rules="{ required: true, trigger: ['blur', 'change'], message: '璇峰~鍐欒〃杈惧紡' }"
+        >
+          <el-input v-model="listenerFieldForm.expression" clearable />
+        </el-form-item>
+      </el-form>
+      <template v-slot:footer>
+        <el-button size="small" @click="listenerFieldFormModelVisible = false">鍙� 娑�</el-button>
+        <el-button size="small" type="primary" @click="saveListenerFiled">纭� 瀹�</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+<script>
+import { createListenerObject, updateElementExtensions } from "../../utils";
+import {
+  initListenerType,
+  initListenerForm,
+  EXECUTION_EVENT_TYPE,
+  LISTENER_TYPE,
+  FIELD_TYPE,
+  SCRIPT_TYPE,
+  EVENT_DEFINITION_TYPE
+} from "./utilSelf";
+import { Plus, Search } from "@element-plus/icons-vue";
+
+export default {
+  name: "ElementListeners",
+  setup() {
+    return { Plus, Search }
+  },
+  props: {
+    id: String,
+    type: String
+  },
+  inject: {
+    prefix: "prefix",
+    width: "width"
+  },
+  data() {
+    return {
+      elementListenersList: [], // 鐩戝惉鍣ㄥ垪琛�
+      listenerForm: {}, // 鐩戝惉鍣ㄨ鎯呰〃鍗�
+      listenerFormModelVisible: false, // 鐩戝惉鍣� 缂栬緫 渚ц竟鏍忔樉绀虹姸鎬�
+      fieldsListOfListener: [],
+      listenerFieldForm: {}, // 鐩戝惉鍣� 娉ㄥ叆瀛楁 璇︽儏琛ㄥ崟
+      listenerFieldFormModelVisible: false, // 鐩戝惉鍣� 娉ㄥ叆瀛楁琛ㄥ崟寮圭獥 鏄剧ず鐘舵��
+      editingListenerIndex: -1, // 鐩戝惉鍣ㄦ墍鍦ㄤ笅鏍囷紝-1 涓烘柊澧�
+      editingListenerFieldIndex: -1, // 瀛楁鎵�鍦ㄤ笅鏍囷紝-1 涓烘柊澧�
+    };
+  },
+  watch: {
+    id: {
+      immediate: true,
+      handler(val) {
+        val && val.length && this.$nextTick(() => this.resetListenersList());
+      }
+    }
+  },
+  computed: {
+    EXECUTION_EVENT_TYPE() { return EXECUTION_EVENT_TYPE },
+    LISTENER_TYPE() { return LISTENER_TYPE },
+    FIELD_TYPE() { return FIELD_TYPE },
+    SCRIPT_TYPE() { return SCRIPT_TYPE },
+    EVENT_DEFINITION_TYPE() { return EVENT_DEFINITION_TYPE },
+    formatterEvent() {
+      return (row) => {
+        return EXECUTION_EVENT_TYPE.find(find => find.value === row.event)?.label || ""
+      }
+    },
+    formatterListener() {
+      return (row) => {
+        return LISTENER_TYPE.find(find => find.value === row.listener)?.label || ""
+      }
+    },
+    formatterField() {
+      return (row) => {
+        return FIELD_TYPE.find(find => find.value === row.fieldType)?.label || ""
+      }
+    },
+    selectedProp() {
+      return LISTENER_TYPE.find(find => find.value === this.listenerForm.listenerType)?.prop || ''
+    }
+  },
+  methods: {
+    resetListenersList() {
+      this.bpmnElement = window.bpmnInstances.bpmnElement;
+      this.otherExtensionList = [];
+      this.bpmnElementListeners =
+        this.bpmnElement.businessObject?.extensionElements?.values?.filter(ex => ex.$type === `${this.prefix}:ExecutionListener`) ?? [];
+      this.elementListenersList = this.bpmnElementListeners.map(listener => initListenerType(listener));
+    },
+    // 鎵撳紑 鐩戝惉鍣ㄨ鎯� 渚ц竟鏍�
+    openListenerForm(listener, index) {
+      if (listener) {
+        this.listenerForm = initListenerForm(listener);
+        this.editingListenerIndex = index;
+      } else {
+        this.listenerForm = {};
+        this.editingListenerIndex = -1; // 鏍囪涓烘柊澧�
+      }
+      if (listener && listener.fields) {
+        this.fieldsListOfListener = listener.fields.map(field => ({
+          ...field,
+          fieldType: field.string ? "string" : "expression"
+        }));
+      } else {
+        this.fieldsListOfListener = [];
+        this.listenerForm["fields"] = []
+      }
+      // 鎵撳紑渚ц竟鏍忓苟娓呮楠岃瘉鐘舵��
+      this.listenerFormModelVisible = true;
+      this.$nextTick(() => {
+        if (this.$refs["listenerFormRef"]) this.$refs["listenerFormRef"].clearValidate();
+      });
+    },
+    // 鎵撳紑鐩戝惉鍣ㄥ瓧娈电紪杈戝脊绐�
+    openListenerFieldForm(field, index) {
+      this.listenerFieldForm = field ? JSON.parse(JSON.stringify(field)) : {};
+      this.editingListenerFieldIndex = field ? index : -1;
+      this.listenerFieldFormModelVisible = true;
+      this.$nextTick(() => {
+        if (this.$refs["listenerFieldFormRef"]) this.$refs["listenerFieldFormRef"].clearValidate();
+      });
+    },
+    // 淇濆瓨鐩戝惉鍣ㄦ敞鍏ュ瓧娈�
+    async saveListenerFiled() {
+      let validateStatus = await this.$refs["listenerFieldFormRef"].validate();
+      if (!validateStatus) return; // 楠岃瘉涓嶉�氳繃鐩存帴杩斿洖
+      if (this.editingListenerFieldIndex === -1) {
+        this.fieldsListOfListener.push(this.listenerFieldForm);
+        this.listenerForm.fields.push(this.listenerFieldForm);
+      } else {
+        this.fieldsListOfListener.splice(this.editingListenerFieldIndex, 1, this.listenerFieldForm);
+        this.listenerForm.fields.splice(this.editingListenerFieldIndex, 1, this.listenerFieldForm);
+      }
+      this.listenerFieldFormModelVisible = false;
+      this.$nextTick(() => (this.listenerFieldForm = {}));
+    },
+    // 绉婚櫎鐩戝惉鍣ㄥ瓧娈�
+    removeListenerField(field, index) {
+      this.$confirm("纭绉婚櫎璇ュ瓧娈靛悧锛�", "鎻愮ず", {
+        confirmButtonText: "纭� 璁�",
+        cancelButtonText: "鍙� 娑�"
+      })
+        .then(() => {
+          this.fieldsListOfListener.splice(index, 1);
+          this.listenerForm.fields.splice(index, 1);
+        })
+        .catch(() => console.info("鎿嶄綔鍙栨秷"));
+    },
+    // 绉婚櫎鐩戝惉鍣�
+    removeListener(listener, index) {
+      this.$confirm("纭绉婚櫎璇ョ洃鍚櫒鍚楋紵", "鎻愮ず", {
+        confirmButtonText: "纭� 璁�",
+        cancelButtonText: "鍙� 娑�"
+      })
+        .then(() => {
+          this.bpmnElementListeners.splice(index, 1);
+          this.elementListenersList.splice(index, 1);
+          updateElementExtensions(this.bpmnElement, this.otherExtensionList.concat(this.bpmnElementListeners));
+        })
+        .catch(() => console.info("鎿嶄綔鍙栨秷"));
+    },
+    // 淇濆瓨鐩戝惉鍣ㄩ厤缃�
+    async saveListenerConfig() {
+      let validateStatus = await this.$refs["listenerFormRef"].validate();
+      if (!validateStatus) return; // 楠岃瘉涓嶉�氳繃鐩存帴杩斿洖
+      const listenerObject = createListenerObject(this.listenerForm, false, this.prefix);
+      if (this.editingListenerIndex === -1) {
+        this.bpmnElementListeners.push(listenerObject);
+        this.elementListenersList.push(this.listenerForm);
+      } else {
+        this.bpmnElementListeners.splice(this.editingListenerIndex, 1, listenerObject);
+        this.elementListenersList.splice(this.editingListenerIndex, 1, this.listenerForm);
+      }
+      // 淇濆瓨鍏朵粬閰嶇疆
+      this.otherExtensionList = this.bpmnElement.businessObject?.extensionElements?.values?.filter(ex => ex.$type !== `${this.prefix}:ExecutionListener`) ?? [];
+      updateElementExtensions(this.bpmnElement, this.otherExtensionList.concat(this.bpmnElementListeners));
+      // 4. 闅愯棌渚ц竟鏍�
+      this.listenerFormModelVisible = false;
+      this.listenerForm = {};
+    }
+  }
+};
+</script>
diff --git a/ruoyi-ui/apps/web-antd/src/package/penal/multi-instance/utilSelf.js b/ruoyi-ui/apps/web-antd/src/package/penal/multi-instance/utilSelf.js
new file mode 100644
index 0000000..a001b9c
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/penal/multi-instance/utilSelf.js
@@ -0,0 +1,80 @@
+// 鍒濆鍖栬〃鍗曟暟鎹�
+export function initListenerForm(listener) {
+  let self = {
+    ...listener
+  };
+  if (listener.script) {
+    self = {
+      ...listener,
+      ...listener.script,
+      scriptType: listener.script.resource ? "externalScript" : "inlineScript"
+    };
+  }
+  if (listener.event === "timeout" && listener.eventDefinitions) {
+    if (listener.eventDefinitions.length) {
+      let k = "";
+      for (let key in listener.eventDefinitions[0]) {
+        console.log(listener.eventDefinitions, key);
+        if (key.indexOf("time") !== -1) {
+          k = key;
+          self.eventDefinitionType = key.replace("time", "").toLowerCase();
+        }
+      }
+      console.log(k);
+      self.eventTimeDefinitions = listener.eventDefinitions[0][k].body;
+    }
+  }
+  return self;
+}
+
+export function initListenerType(listener) {
+  let listenerType;
+  if (listener.class) listenerType = "classListener";
+  if (listener.expression) listenerType = "expressionListener";
+  if (listener.delegateExpression) listenerType = "delegateExpressionListener";
+  if (listener.script) listenerType = "scriptListener";
+  return {
+    ...JSON.parse(JSON.stringify(listener)),
+    ...(listener.script ?? {}),
+    listenerType: listenerType
+  };
+}
+
+// 鐩戝惉绫诲瀷
+export const LISTENER_TYPE = [
+  { label: "Java 绫�", value: "classListener", prop: "class", key: "listener-class" },
+  { label: "琛ㄨ揪寮�", value: "expressionListener", prop: "expression", key: "listener-expression" },
+  { label: "浠g悊琛ㄨ揪寮�", value: "delegateExpressionListener", prop: "delegateExpression", key: "listener-delegate" },
+  // { label: "鑴氭湰", value: "scriptListener", prop: "scriptFormat", key: "listener-script-format" },
+]
+// 鑴氭湰绫诲瀷
+export const SCRIPT_TYPE = [
+  { label: "鍐呰仈鑴氭湰", value: "inlineScript" },
+  { label: "澶栭儴鑴氭湰", value: "externalScript" },
+]
+// 浠诲姟鐩戝惉鍣�: 浜嬩欢绫诲瀷
+export const TASK_EVENT_TYPE = [
+  { label: "鍒涘缓", value: "create" },
+  { label: "鎸囨淳", value: "assignment" },
+  { label: "瀹屾垚", value: "complete" },
+  { label: "鍒犻櫎", value: "delete" },
+  { label: "鏇存柊", value: "update" },
+  { label: "瓒呮椂", value: "timeout" },
+]
+// 鎵ц鐩戝惉鍣�: 浜嬩欢绫诲瀷
+export const EXECUTION_EVENT_TYPE = [
+  { label: "寮�濮�", value: "start" },
+  { label: "缁撴潫", value: "end" },
+]
+// 浜嬩欢绫诲瀷: 瀹氭椂鍣ㄧ被鍨�
+export const EVENT_DEFINITION_TYPE = [
+  { label: "鏃�", value: "null" },
+  { label: "鏃ユ湡", value: "date" },
+  { label: "鎸佺画鏃堕暱", value: "duration" },
+  { label: "寰幆", value: "cycle" },
+]
+// 瀛楁閰嶇疆
+export const FIELD_TYPE = [
+  { label: "瀛楃涓�", value: "string" },
+  { label: "琛ㄨ揪寮�", value: "expression" },
+]
diff --git a/ruoyi-ui/apps/web-antd/src/package/penal/other/ElementOtherConfig.vue b/ruoyi-ui/apps/web-antd/src/package/penal/other/ElementOtherConfig.vue
new file mode 100644
index 0000000..7016a06
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/penal/other/ElementOtherConfig.vue
@@ -0,0 +1,59 @@
+<template>
+  <div class="panel-tab__content">
+    <div class="element-property input-property">
+      <div class="element-property__label">鍏冪礌鏂囨。锛�</div>
+      <div class="element-property__value">
+        <el-input
+          type="textarea"
+          v-model="documentation"
+          size="small"
+          resize="vertical"
+          :autosize="{ minRows: 2, maxRows: 4 }"
+          @input="updateDocumentation"
+          @blur="updateDocumentation"
+        />
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "ElementOtherConfig",
+  props: {
+    id: String
+  },
+  data() {
+    return {
+      documentation: ""
+    };
+  },
+  watch: {
+    id: {
+      immediate: true,
+      handler: function(id) {
+        if (id && id.length) {
+          this.$nextTick(() => {
+            const documentations = window.bpmnInstances.bpmnElement.businessObject?.documentation;
+            this.documentation = documentations && documentations.length ? documentations[0].text : "";
+          });
+        } else {
+          this.documentation = "";
+        }
+      }
+    }
+  },
+  methods: {
+    updateDocumentation() {
+      (this.bpmnElement && this.bpmnElement.id === this.id) || (this.bpmnElement = window.bpmnInstances.elementRegistry.get(this.id));
+      const documentation = window.bpmnInstances.bpmnFactory.create("bpmn:Documentation", { text: this.documentation });
+      window.bpmnInstances.modeling.updateProperties(this.bpmnElement, {
+        documentation: [documentation]
+      });
+    }
+  },
+  beforeUnmount() {
+    this.bpmnElement = null;
+  }
+};
+</script>
diff --git a/ruoyi-ui/apps/web-antd/src/package/penal/properties/ElementProperties.vue b/ruoyi-ui/apps/web-antd/src/package/penal/properties/ElementProperties.vue
new file mode 100644
index 0000000..a8715b8
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/penal/properties/ElementProperties.vue
@@ -0,0 +1,141 @@
+<template>
+  <div class="panel-tab__content">
+    <el-table :data="elementPropertyList" size="small" max-height="240" border fit>
+      <el-table-column label="搴忓彿" width="50px" type="index" />
+      <el-table-column label="灞炴�у悕" prop="name" min-width="100px" show-overflow-tooltip />
+      <el-table-column label="灞炴�у��" prop="value" min-width="100px" show-overflow-tooltip />
+      <el-table-column label="鎿嶄綔" width="90px">
+        <template v-slot="{ row, $index }">
+          <el-button link type="" @click="openAttributesForm(row, $index)">缂栬緫</el-button>
+          <el-divider direction="vertical" />
+          <el-button link type="" style="color: #ff4d4f" @click="removeAttributes(row, $index)">绉婚櫎</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <div class="element-drawer__button">
+      <el-button size="small" type="primary" :icon="Plus" @click="openAttributesForm(null, -1)">娣诲姞灞炴��</el-button>
+    </div>
+
+    <el-dialog v-model="propertyFormModelVisible" title="灞炴�ч厤缃�" width="600px" append-to-body destroy-on-close>
+      <el-form :model="propertyForm" label-width="80px" size="small" ref="attributeFormRef" @submit.prevent>
+        <el-form-item label="灞炴�у悕锛�" prop="name">
+          <el-input v-model="propertyForm.name" clearable />
+        </el-form-item>
+        <el-form-item label="灞炴�у�硷細" prop="value">
+          <el-input v-model="propertyForm.value" clearable />
+        </el-form-item>
+      </el-form>
+      <template v-slot:footer>
+        <el-button size="small" @click="propertyFormModelVisible = false">鍙� 娑�</el-button>
+        <el-button size="small" type="primary" @click="saveAttribute">纭� 瀹�</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { Plus } from '@element-plus/icons-vue'
+export default {
+  name: "ElementProperties",
+  setup() {
+    return {
+      Plus
+    }
+  },
+  props: {
+    id: String,
+    type: String
+  },
+  inject: {
+    prefix: "prefix",
+    width: "width"
+  },
+  data() {
+    return {
+      elementPropertyList: [],
+      propertyForm: {},
+      editingPropertyIndex: -1,
+      propertyFormModelVisible: false
+    };
+  },
+  watch: {
+    id: {
+      immediate: true,
+      handler(val) {
+        val && val.length && this.resetAttributesList();
+      }
+    }
+  },
+  methods: {
+    resetAttributesList() {
+      this.bpmnElement = window.bpmnInstances.bpmnElement;
+      this.otherExtensionList = []; // 鍏朵粬鎵╁睍閰嶇疆
+      this.bpmnElementProperties =
+        this.bpmnElement.businessObject?.extensionElements?.values?.filter(ex => {
+          if (ex.$type !== `${this.prefix}:Properties`) {
+            this.otherExtensionList.push(ex);
+          }
+          return ex.$type === `${this.prefix}:Properties`;
+        }) ?? [];
+
+      // 淇濆瓨鎵�鏈夌殑 鎵╁睍灞炴�у瓧娈�
+      this.bpmnElementPropertyList = this.bpmnElementProperties.reduce((pre, current) => pre.concat(current.values), []);
+      // 澶嶅埗 鏄剧ず
+      this.elementPropertyList = JSON.parse(JSON.stringify(this.bpmnElementPropertyList ?? []));
+    },
+    openAttributesForm(attr, index) {
+      this.editingPropertyIndex = index;
+      this.propertyForm = index === -1 ? {} : JSON.parse(JSON.stringify(attr));
+      this.propertyFormModelVisible = true;
+      this.$nextTick(() => {
+        if (this.$refs["attributeFormRef"]) this.$refs["attributeFormRef"].clearValidate();
+      });
+    },
+    removeAttributes(attr, index) {
+      this.$confirm("纭绉婚櫎璇ュ睘鎬у悧锛�", "鎻愮ず", {
+        confirmButtonText: "纭� 璁�",
+        cancelButtonText: "鍙� 娑�"
+      })
+        .then(() => {
+          this.elementPropertyList.splice(index, 1);
+          this.bpmnElementPropertyList.splice(index, 1);
+          // 鏂板缓涓�涓睘鎬у瓧娈电殑淇濆瓨鍒楄〃
+          const propertiesObject = window.bpmnInstances.moddle.create(`${this.prefix}:Properties`, {
+            values: this.bpmnElementPropertyList
+          });
+          this.updateElementExtensions(propertiesObject);
+          this.resetAttributesList();
+        })
+        .catch(() => console.info("鎿嶄綔鍙栨秷"));
+    },
+    saveAttribute() {
+      const { name, value } = this.propertyForm;
+      console.log(this.bpmnElementPropertyList);
+      if (this.editingPropertyIndex !== -1) {
+        window.bpmnInstances.modeling.updateModdleProperties(this.bpmnElement, this.bpmnElementPropertyList[this.editingPropertyIndex], {
+          name,
+          value
+        });
+      } else {
+        // 鏂板缓灞炴�у瓧娈�
+        const newPropertyObject = window.bpmnInstances.moddle.create(`${this.prefix}:Property`, { name, value });
+        // 鏂板缓涓�涓睘鎬у瓧娈电殑淇濆瓨鍒楄〃
+        const propertiesObject = window.bpmnInstances.moddle.create(`${this.prefix}:Properties`, {
+          values: this.bpmnElementPropertyList.concat([newPropertyObject])
+        });
+        this.updateElementExtensions(propertiesObject);
+      }
+      this.propertyFormModelVisible = false;
+      this.resetAttributesList();
+    },
+    updateElementExtensions(properties) {
+      const extensions = window.bpmnInstances.moddle.create("bpmn:ExtensionElements", {
+        values: this.otherExtensionList.concat([properties])
+      });
+      window.bpmnInstances.modeling.updateProperties(this.bpmnElement, {
+        extensionElements: extensions
+      });
+    }
+  }
+};
+</script>
diff --git a/ruoyi-ui/apps/web-antd/src/package/penal/signal-message/SignalAndMessage.vue b/ruoyi-ui/apps/web-antd/src/package/penal/signal-message/SignalAndMessage.vue
new file mode 100644
index 0000000..663788e
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/penal/signal-message/SignalAndMessage.vue
@@ -0,0 +1,110 @@
+<template>
+  <div class="panel-tab__content">
+    <div class="panel-tab__content--title">
+      <span><el-icon style="margin-right: 8px; color: #555555"><Menu /></el-icon>娑堟伅鍒楄〃</span>
+      <el-button size="small" type="primary" :icon="Plus" @click="openModel('message')">鍒涘缓鏂版秷鎭�</el-button>
+    </div>
+    <el-table :data="messageList" size="small" border>
+      <el-table-column type="index" label="搴忓彿" width="60px" />
+      <el-table-column label="娑堟伅ID" prop="id" max-width="300px" show-overflow-tooltip />
+      <el-table-column label="娑堟伅鍚嶇О" prop="name" max-width="300px" show-overflow-tooltip />
+    </el-table>
+    <div class="panel-tab__content--title" style="padding-top: 8px; margin-top: 8px; border-top: 1px solid #eeeeee">
+      <span><el-icon style="margin-right: 8px; color: #555555"><Menu /></el-icon>淇″彿鍒楄〃</span>
+      <el-button size="small" type="primary" :icon="Plus" @click="openModel('signal')">鍒涘缓鏂颁俊鍙�</el-button>
+    </div>
+    <el-table :data="signalList" size="small" border>
+      <el-table-column type="index" label="搴忓彿" width="60px" />
+      <el-table-column label="淇″彿ID" prop="id" max-width="300px" show-overflow-tooltip />
+      <el-table-column label="淇″彿鍚嶇О" prop="name" max-width="300px" show-overflow-tooltip />
+    </el-table>
+
+    <el-dialog v-model="modelVisible" :title="modelConfig.title" :close-on-click-modal="false" width="400px" append-to-body destroy-on-close>
+      <el-form :model="modelObjectForm" size="small" label-width="90px" @submit.prevent>
+        <el-form-item :label="modelConfig.idLabel">
+          <el-input v-model="modelObjectForm.id" clearable />
+        </el-form-item>
+        <el-form-item :label="modelConfig.nameLabel">
+          <el-input v-model="modelObjectForm.name" clearable />
+        </el-form-item>
+      </el-form>
+      <template v-slot:footer>
+        <el-button size="small" @click="modelVisible = false">鍙� 娑�</el-button>
+        <el-button size="small" type="primary" @click="addNewObject">淇� 瀛�</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+<script>
+import { Plus } from '@element-plus/icons-vue'
+export default {
+  name: 'SignalAndMassage',
+  setup() {
+    return {
+      Plus
+    }
+  },
+  data() {
+    return {
+      signalList: [],
+      messageList: [],
+      modelVisible: false,
+      modelType: '',
+      modelObjectForm: {}
+    };
+  },
+  computed: {
+    modelConfig() {
+      if (this.modelType === 'message') {
+        return { title: '鍒涘缓娑堟伅', idLabel: '娑堟伅ID', nameLabel: '娑堟伅鍚嶇О' };
+      } else {
+        return { title: '鍒涘缓淇″彿', idLabel: '淇″彿ID', nameLabel: '淇″彿鍚嶇О' };
+      }
+    }
+  },
+  mounted() {
+    this.initDataList();
+  },
+  methods: {
+    initDataList() {
+      this.rootElements = window.bpmnInstances.modeler.getDefinitions().rootElements;
+      this.messageIdMap = {};
+      this.signalIdMap = {};
+      this.messageList = [];
+      this.signalList = [];
+      this.rootElements.forEach(el => {
+        if (el.$type === 'bpmn:Message') {
+          this.messageIdMap[el.id] = true;
+          this.messageList.push({ ...el });
+        }
+        if (el.$type === 'bpmn:Signal') {
+          this.signalIdMap[el.id] = true;
+          this.signalList.push({ ...el });
+        }
+      });
+    },
+    openModel(type) {
+      this.modelType = type;
+      this.modelObjectForm = {};
+      this.modelVisible = true;
+    },
+    addNewObject() {
+      if (this.modelType === 'message') {
+        if (this.messageIdMap[this.modelObjectForm.id]) {
+          return this.$message.error('璇ユ秷鎭凡瀛樺湪锛岃淇敼id鍚庨噸鏂颁繚瀛�');
+        }
+        const messageRef = window.bpmnInstances.moddle.create('bpmn:Message', this.modelObjectForm);
+        this.rootElements.push(messageRef);
+      } else {
+        if (this.signalIdMap[this.modelObjectForm.id]) {
+          return this.$message.error('璇ヤ俊鍙峰凡瀛樺湪锛岃淇敼id鍚庨噸鏂颁繚瀛�');
+        }
+        const signalRef = window.bpmnInstances.moddle.create('bpmn:Signal', this.modelObjectForm);
+        this.rootElements.push(signalRef);
+      }
+      this.modelVisible = false;
+      this.initDataList();
+    }
+  }
+};
+</script>
diff --git a/ruoyi-ui/apps/web-antd/src/package/penal/task/ElementTask.vue b/ruoyi-ui/apps/web-antd/src/package/penal/task/ElementTask.vue
new file mode 100644
index 0000000..b651f4b
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/penal/task/ElementTask.vue
@@ -0,0 +1,72 @@
+<template>
+  <div class="panel-tab__content">
+    <el-form size="small" label-width="90px" @submit.prevent>
+<!--      <el-form-item label="寮傛寤剁画">-->
+<!--        <el-checkbox v-model="taskConfigForm.asyncBefore" label="寮傛鍓�" @change="changeTaskAsync" />-->
+<!--        <el-checkbox v-model="taskConfigForm.asyncAfter" label="寮傛鍚�" @change="changeTaskAsync" />-->
+<!--        <el-checkbox v-model="taskConfigForm.exclusive" v-if="taskConfigForm.asyncAfter || taskConfigForm.asyncBefore" label="鎺掗櫎" @change="changeTaskAsync" />-->
+<!--      </el-form-item>-->
+      <component :is="witchTaskComponent" v-bind="$props" />
+    </el-form>
+  </div>
+</template>
+
+<script>
+import UserTask from "./task-components/UserTask.vue";
+import ScriptTask from "./task-components/ScriptTask.vue";
+import ReceiveTask from "./task-components/ReceiveTask.vue";
+
+export default {
+  name: "ElementTaskConfig",
+  components: { UserTask, ScriptTask, ReceiveTask },
+  props: {
+    id: String,
+    type: String
+  },
+  data() {
+    return {
+      taskConfigForm: {
+        asyncAfter: false,
+        asyncBefore: false,
+        exclusive: false
+      },
+      witchTaskComponent: "",
+      installedComponent: {
+        // 鎵嬪伐浠诲姟涓庢櫘閫氫换鍔′竴鑷达紝涓嶉渶瑕佸叾浠栭厤缃�
+        // 鎺ユ敹娑堟伅浠诲姟锛岄渶瑕佸湪鍏ㄥ眬涓嬫彃鍏ユ柊鐨勬秷鎭疄渚嬶紝骞跺湪璇ヨ妭鐐逛笅鐨� messageRef 灞炴�х粦瀹氳瀹炰緥
+        // 鍙戦�佷换鍔°�佹湇鍔′换鍔°�佷笟鍔¤鍒欎换鍔″叡鐢ㄤ竴涓浉鍚岄厤缃�
+        UserTask: "UserTask", // 鐢ㄦ埛浠诲姟閰嶇疆
+        ScriptTask: "ScriptTask", // 鑴氭湰浠诲姟閰嶇疆
+        ReceiveTask: "ReceiveTask" // 娑堟伅鎺ユ敹浠诲姟
+      }
+    };
+  },
+  watch: {
+    id: {
+      immediate: true,
+      handler() {
+        this.bpmnElement = window.bpmnInstances.bpmnElement;
+        // this.taskConfigForm.asyncBefore = this.bpmnElement?.businessObject?.asyncBefore;
+        // this.taskConfigForm.asyncAfter = this.bpmnElement?.businessObject?.asyncAfter;
+        // this.taskConfigForm.exclusive = this.bpmnElement?.businessObject?.exclusive;
+      }
+    },
+    type: {
+      immediate: true,
+      handler() {
+        this.witchTaskComponent = this.installedComponent[this.$props.type];
+      }
+    }
+  },
+  methods: {
+    changeTaskAsync() {
+      if (!this.taskConfigForm.asyncBefore && !this.taskConfigForm.asyncAfter) {
+        this.taskConfigForm.exclusive = false;
+      }
+      window.bpmnInstances.modeling.updateProperties(window.bpmnInstances.bpmnElement, {
+        ...this.taskConfigForm
+      });
+    }
+  }
+};
+</script>
diff --git a/ruoyi-ui/apps/web-antd/src/package/penal/task/task-components/ReceiveTask.vue b/ruoyi-ui/apps/web-antd/src/package/penal/task/task-components/ReceiveTask.vue
new file mode 100644
index 0000000..c40cbe3
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/penal/task/task-components/ReceiveTask.vue
@@ -0,0 +1,102 @@
+<template>
+  <div style="margin-top: 16px">
+    <el-form-item label="娑堟伅瀹炰緥">
+      <div style="display: flex; align-items: center; justify-content: space-between; flex-wrap: nowrap">
+        <el-select v-model="bindMessageId" @change="updateTaskMessage">
+          <el-option v-for="id in Object.keys(messageMap)" :value="id" :label="messageMap[id]" :key="id" />
+        </el-select>
+        <el-button size="small" type="primary" :icon="Plus" style="margin-left: 8px" @click="openMessageModel" />
+      </div>
+    </el-form-item>
+    <el-dialog v-model="messageModelVisible" :close-on-click-modal="false" title="鍒涘缓鏂版秷鎭�" width="400px" append-to-body destroy-on-close>
+      <el-form :model="newMessageForm" size="small" label-width="90px" @submit.prevent>
+        <el-form-item label="娑堟伅ID">
+          <el-input v-model="newMessageForm.id" clearable />
+        </el-form-item>
+        <el-form-item label="娑堟伅鍚嶇О">
+          <el-input v-model="newMessageForm.name" clearable />
+        </el-form-item>
+      </el-form>
+      <template v-slot:footer>
+        <el-button size="small" type="primary" @click="createNewMessage">纭� 璁�</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "ReceiveTask",
+  setup() {
+    return {
+      Plus
+    }
+  },
+  props: {
+    id: String,
+    type: String
+  },
+  data() {
+    return {
+      bindMessageId: "",
+      newMessageForm: {},
+      messageMap: {},
+      messageModelVisible: false
+    };
+  },
+  watch: {
+    id: {
+      immediate: true,
+      handler() {
+        this.$nextTick(() => this.getBindMessage());
+      }
+    }
+  },
+  created() {
+    this.bpmnMessageRefsMap = Object.create(null);
+    this.bpmnRootElements = window.bpmnInstances.modeler.getDefinitions().rootElements;
+    this.bpmnRootElements
+      .filter(el => el.$type === "bpmn:Message")
+      .forEach(m => {
+        this.bpmnMessageRefsMap[m.id] = m;
+        this.messageMap[m.id] = m.name
+      });
+    this.messageMap["-1"] = "鏃�" // 娣诲姞涓�涓┖瀵硅薄锛屼繚璇佸彲浠ュ彇娑堝師娑堟伅缁戝畾
+  },
+  methods: {
+    getBindMessage() {
+      this.bpmnElement = window.bpmnInstances.bpmnElement;
+      this.bindMessageId = this.bpmnElement.businessObject?.messageRef?.id || "-1";
+    },
+    openMessageModel() {
+      this.messageModelVisible = true;
+      this.newMessageForm = {};
+    },
+    createNewMessage() {
+      if (this.messageMap[this.newMessageForm.id]) {
+        this.$message.error("璇ユ秷鎭凡瀛樺湪锛岃淇敼id鍚庨噸鏂颁繚瀛�");
+        return;
+      }
+      const newMessage = window.bpmnInstances.moddle.create("bpmn:Message", this.newMessageForm);
+      this.bpmnRootElements.push(newMessage);
+      this.messageMap[this.newMessageForm.id] = this.newMessageForm.name
+      this.bpmnMessageRefsMap[this.newMessageForm.id] = newMessage;
+      this.messageModelVisible = false;
+    },
+    updateTaskMessage(messageId) {
+      if (messageId === "-1") {
+        window.bpmnInstances.modeling.updateProperties(this.bpmnElement, {
+          messageRef: null
+        });
+      } else {
+        window.bpmnInstances.modeling.updateProperties(this.bpmnElement, {
+          messageRef: this.bpmnMessageRefsMap[messageId]
+        });
+      }
+    }
+  },
+  beforeUnmount() {
+    this.bpmnElement = null;
+  }
+};
+</script>
diff --git a/ruoyi-ui/apps/web-antd/src/package/penal/task/task-components/ScriptTask.vue b/ruoyi-ui/apps/web-antd/src/package/penal/task/task-components/ScriptTask.vue
new file mode 100644
index 0000000..2239219
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/penal/task/task-components/ScriptTask.vue
@@ -0,0 +1,85 @@
+<template>
+  <div style="margin-top: 16px">
+    <el-form-item label="鑴氭湰鏍煎紡">
+      <el-input v-model="scriptTaskForm.scriptFormat" clearable @input="updateElementTask()" @change="updateElementTask()" />
+    </el-form-item>
+    <el-form-item label="鑴氭湰绫诲瀷">
+      <el-select v-model="scriptTaskForm.scriptType">
+        <el-option label="鍐呰仈鑴氭湰" value="inline" />
+        <el-option label="澶栭儴璧勬簮" value="external" />
+      </el-select>
+    </el-form-item>
+    <el-form-item label="鑴氭湰" v-show="scriptTaskForm.scriptType === 'inline'">
+      <el-input
+        v-model="scriptTaskForm.script"
+        type="textarea"
+        resize="vertical"
+        :autosize="{ minRows: 2, maxRows: 4 }"
+        clearable
+        @input="updateElementTask()"
+        @change="updateElementTask()"
+      />
+    </el-form-item>
+    <el-form-item label="璧勬簮鍦板潃" v-show="scriptTaskForm.scriptType === 'external'">
+      <el-input v-model="scriptTaskForm.resource" clearable @input="updateElementTask()" @change="updateElementTask()" />
+    </el-form-item>
+    <el-form-item label="缁撴灉鍙橀噺">
+      <el-input v-model="scriptTaskForm.resultVariable" clearable @input="updateElementTask()" @change="updateElementTask()" />
+    </el-form-item>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "ScriptTask",
+  props: {
+    id: String,
+    type: String
+  },
+  data() {
+    return {
+      defaultTaskForm: {
+        scriptFormat: "",
+        script: "",
+        resource: "",
+        resultVariable: ""
+      },
+      scriptTaskForm: {}
+    };
+  },
+  watch: {
+    id: {
+      immediate: true,
+      handler() {
+        this.bpmnElement = window.bpmnInstances.bpmnElement;
+        this.$nextTick(() => this.resetTaskForm());
+      }
+    }
+  },
+  methods: {
+    resetTaskForm() {
+      for (let key in this.defaultTaskForm) {
+        let value = this.bpmnElement?.businessObject[key] || this.defaultTaskForm[key];
+        this.scriptTaskForm[key] = value
+      }
+      this.scriptTaskForm["scriptType"] = this.scriptTaskForm.script ? "inline" : "external"
+    },
+    updateElementTask() {
+      let taskAttr = Object.create(null);
+      taskAttr.scriptFormat = this.scriptTaskForm.scriptFormat || null;
+      taskAttr.resultVariable = this.scriptTaskForm.resultVariable || null;
+      if (this.scriptTaskForm.scriptType === "inline") {
+        taskAttr.script = this.scriptTaskForm.script || null;
+        taskAttr.resource = null;
+      } else {
+        taskAttr.resource = this.scriptTaskForm.resource || null;
+        taskAttr.script = null;
+      }
+      window.bpmnInstances.modeling.updateProperties(this.bpmnElement, taskAttr);
+    }
+  },
+  beforeUnmount() {
+    this.bpmnElement = null;
+  }
+};
+</script>
diff --git a/ruoyi-ui/apps/web-antd/src/package/penal/task/task-components/UserTask.vue b/ruoyi-ui/apps/web-antd/src/package/penal/task/task-components/UserTask.vue
new file mode 100644
index 0000000..98b379b
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/penal/task/task-components/UserTask.vue
@@ -0,0 +1,684 @@
+<template>
+  <div style="margin-top: 16px">
+    <el-row>
+      <h4 class="mb-10 fw-700">瀹℃壒浜鸿缃�</h4>
+      <el-radio-group v-model="dataType" @change="changeDataType">
+        <el-radio
+          v-for="item in USER_TASK_GROUP"
+          :key="item.value"
+          :label="item.value"
+        >{{item.label}}</el-radio>
+      </el-radio-group>
+    </el-row>
+    <el-row>
+      <el-col :span="24" v-if="dataType === USER_TASK_GROUP[0].value">
+        <el-tag v-for="userText in selectedUser.text" :key="userText" effect="plain" closable @close="handleClose(userText)">
+          {{userText}}
+        </el-tag>
+        <div class="element-drawer__button">
+          <el-button size="default" type="primary" :icon="Plus" @click="onSelectUsers()">娣诲姞鐢ㄦ埛</el-button>
+        </div>
+      </el-col>
+      <el-col :span="24" v-else-if="dataType === USER_TASK_GROUP[1].value">
+        <el-select v-model="roleIds" multiple size="default" placeholder="璇烽�夋嫨瑙掕壊" @change="changeSelectRoles" style="width: 100%">
+          <el-option
+            v-for="item in roleOptions"
+            :key="item.roleId"
+            :label="item.roleName"
+            :value="`ROLE${item.roleId}`"
+            :disabled="item.status === 1">
+          </el-option>
+        </el-select>
+      </el-col>
+      <el-col :span="24" v-else-if="dataType === USER_TASK_GROUP[2].value">
+        <el-tree-select
+          v-model="deptIds"
+          :data="deptTreeData"
+          :props="deptProps"
+           value-key="id"
+          聽placeholder="閫夋嫨閮ㄩ棬"
+           check-strictly
+           clearable
+           @clear="clearDept"
+           ref="selectTree"
+           @node-click="checkedDeptChangeNode"
+           />
+
+
+      </el-col>
+    </el-row>
+    <el-row>
+      <el-col :span="24" v-show="showMultiFlog">
+        <el-divider />
+        <h4><b>澶氬疄渚嬪鎵规柟寮�</b></h4>
+        <el-row>
+          <el-radio-group v-model="multiLoopType" @change="changeMultiLoopType()" class="flex-column radio-group-flex">
+            <el-row v-for="item in MULTI_INSTANCE_TYPE" :key="item.value">
+              <el-radio :label="item.value">{{item.label}}</el-radio>
+            </el-row>
+          </el-radio-group>
+        </el-row>
+        <el-row v-if="multiLoopType !== MULTI_INSTANCE_TYPE[0].value">
+          <el-tooltip content="寮�鍚悗锛屽疄渚嬮渶鎸夐『搴忚疆娴佸鎵�" placement="top-start" @click.stop.prevent>
+            <el-icon><InfoFilled /></el-icon>
+          </el-tooltip>
+          <span class="custom-label">椤哄簭瀹℃壒锛�</span>
+          <el-switch v-model="isSequential" @change="changeMultiLoopType()" />
+        </el-row>
+      </el-col>
+    </el-row>
+
+    <!-- 鍊欓�夌敤鎴峰脊绐� -->
+    <el-dialog title="鍊欓�夌敤鎴�" v-model="userOpen" width="60%" append-to-body>
+      <el-row type="flex" :gutter="20">
+        <!--閮ㄩ棬鏁版嵁-->
+        <el-col :span="7">
+          <el-card shadow="never" style="height: 100%">
+            <div class="head-container">
+              <el-input
+                v-model="queryParamsDeptIds"
+                placeholder="璇疯緭鍏ラ儴闂ㄥ悕绉�"
+                clearable
+                size="small"
+                :prefix-icon="Search"
+                style="margin-bottom: 20px"
+              />
+              <el-tree
+                :data="deptOptions"
+                :props="deptProps"
+                :expand-on-click-node="false"
+                :filter-node-method="filterNode"
+                ref="deptTree"
+                :default-expand-all="false"
+                node-key="id"
+                highlight-current
+                @current-change="handleNodeClick"
+              />
+            </div>
+          </el-card>
+        </el-col>
+        <el-col :span="17">
+          <el-form
+            :model="queryParams"
+            ref="queryForm"
+            size="small"
+            :inline="true"
+            label-width="68px"
+          >
+            <el-form-item label="鐢ㄦ埛鍚嶇О" prop="nickName">
+              <el-input
+                v-model="queryParams.nickName"
+                placeholder="璇疯緭鍏ョ敤鎴峰悕绉�"
+                clearable
+                style="width: 150px"
+                @keyup.enter.native="handleQuery"
+              />
+            </el-form-item>
+            <el-form-item>
+              <el-button
+                type="primary"
+                :icon="Search"
+                @click="handleQuery"
+              >鎼滅储</el-button>
+              <el-button
+                :icon="Refresh"
+                @click="resetQuery"
+              >閲嶇疆</el-button>
+            </el-form-item>
+          </el-form>
+          <el-table
+            ref="multipleTable"
+            height="600"
+            v-loading="tableLoading"
+            :data="userTableList"
+            border
+            @row-click="clickRow"
+            @selection-change="handleSelectionChange">
+            <el-table-column type="selection" width="50" align="center" />
+            <el-table-column label="鐢ㄦ埛鍚�" align="center" prop="nickName" />
+            <el-table-column label="閮ㄩ棬" align="center" prop="dept.deptName" />
+          </el-table>
+          <pagination
+            :total="userTotal"
+            v-model:page="queryParams.pageNum"
+            v-model:limit="queryParams.pageSize"
+            @pagination="getList"
+          />
+        </el-col>
+      </el-row>
+      <template #footer class="dialog-footer">
+        <el-button type="primary" size="default" @click="handleTaskUserComplete">纭� 瀹�</el-button>
+        <el-button size="default" @click="userOpen = false">鍙� 娑�</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+<script>
+import { userList, getDeptTree } from "#/api/system/user";
+import { roleList } from "#/api/system/role";
+import { uniqBy } from 'lodash'
+
+const USER_TASK_GROUP = [
+  {
+    label: "鎸囧畾鐢ㄦ埛",
+    value: "USERS",
+  },
+  {
+    label: "瑙掕壊",
+    value: "ROLES",
+  },
+  {
+    label: "閮ㄩ棬",
+    value: "DEPTS",
+  },
+  {
+    label: "鍙戣捣浜�",
+    value: "INITIATOR",
+  },
+]
+const USER_TASK_FORM = {
+  dataType: '',
+  assignee: '',
+  candidateUsers: '',
+  candidateGroups: '',
+  text: '',
+  deptId: '',
+  // dueDate: '',
+  // followUpDate: '',
+  // priority: ''
+}
+const MULTI_INSTANCE_TYPE = [
+  { label: "鏃�", value: 'Null' },
+  { label: "浼氱锛堥渶鎵�鏈夊鎵逛汉鍚屾剰锛�", value: 'SequentialMultiInstance' },
+  { label: "鎴栫锛堜竴鍚嶅鎵逛汉鍚屾剰鍗冲彲锛�", value: 'ParallelMultiInstance' },
+  { label: "鑷畾涔�", value: 'DiyMultiInstance' },
+]
+export default {
+  name: "UserTask",
+  props: {
+    id: String,
+    type: String
+  },
+  setup() {
+
+    return { Plus, Search, Refresh  }
+  },
+  data() {
+    return {
+      tableLoading: false,
+      dataType: USER_TASK_GROUP[0].value,
+      selectedUser: {
+        ids: [],
+        text: []
+      },
+      userOpen: false,
+      deptName: undefined,
+      deptOptions: [], //閮ㄩ棬鍒楄〃
+      deptProps: {
+        children: "children",
+        label: "label",
+        value: 'id'
+      },
+      userTableList: [],
+      userTotal: 0,
+      selectedUserDate: [],
+      roleOptions: [],
+      roleIds: [],
+      deptTreeData: [],
+      deptIds: [],
+      queryParamsDeptIds:[],
+      // 鏌ヨ鍙傛暟
+      queryParams: {
+        deptId: undefined,
+        nickName: undefined,
+        pageNum: 1,
+        pageSize: 20,
+      },
+      showMultiFlog: false,
+      isSequential: false,
+      multiLoopType: MULTI_INSTANCE_TYPE[0].value,
+    };
+  },
+  watch: {
+    id: {
+      immediate: true,
+      handler() {
+        this.bpmnElement = window.bpmnInstances.bpmnElement;
+        this.$nextTick(() => this.resetTaskForm());
+      }
+    },
+    // 鏍规嵁鍚嶇О绛涢�夐儴闂ㄦ爲
+    queryParamsDeptIds(val) {
+      this.$nextTick(() => {
+        this.$refs.deptTree.filter(val);
+     })
+    }
+  },
+  computed: {
+    USER_TASK_GROUP() { return USER_TASK_GROUP },
+    MULTI_INSTANCE_TYPE() { return MULTI_INSTANCE_TYPE }
+  },
+  methods: {
+    // 鍒濆鍖栧彉閲�
+    async resetTaskForm() {
+      const bpmnElementObj = this.bpmnElement?.businessObject;
+      if (!bpmnElementObj) {
+        return;
+      }
+      this.clearOptionsData()
+      this.dataType = bpmnElementObj.dataType;
+      if (this.dataType === USER_TASK_GROUP[0].value) {
+        let userIdData = bpmnElementObj['candidateUsers'] || bpmnElementObj['assignee'];
+        let userText = bpmnElementObj.text || [];
+        if (userIdData && userIdData.toString().length > 0 && userText && userText.length > 0) {
+          this.selectedUser.ids = userIdData.toString().split(',');
+          this.selectedUser.text = userText.split(',');
+        }
+        if (this.selectedUser.ids.length > 1) {
+          this.showMultiFlog = true;
+        }
+      } else if (this.dataType === USER_TASK_GROUP[1].value) {
+        // 鑾峰彇瑙掕壊鍒楄〃
+        this.getRoleOptions();
+        let roleIdData = bpmnElementObj['candidateGroups'] || [];
+        if (roleIdData && roleIdData.length > 0) {
+          this.roleIds = roleIdData.split(',')
+        }
+        this.showMultiFlog = true;
+      } else if (this.dataType === USER_TASK_GROUP[2].value) {
+        // 鑾峰彇閮ㄩ棬鍒楄〃
+        await this.getDeptTreeData();
+        let deptIdData = bpmnElementObj['candidateGroups'] || [];
+        if (deptIdData && deptIdData.length > 0) {
+          this.deptIds = deptIdData.split(',');
+          // 鍥炴樉
+          this.checkedDeptChange(this.deptIds);
+        }
+        this.showMultiFlog = true;
+      }
+      this.getElementLoop(bpmnElementObj);
+    },
+    // 娓呯┖閫夐」鏁版嵁
+    clearOptionsData() {
+      this.selectedUser.ids = [];
+      this.selectedUser.text = [];
+      this.roleIds = [];
+      this.deptIds = [];
+    },
+    // 鏇存柊鑺傜偣鏁版嵁
+    updateElementTask() {
+      const taskObj = Object.assign({}, { ...USER_TASK_FORM })
+      window.bpmnInstances.modeling.updateProperties(this.bpmnElement, taskObj);
+    },
+    // 鏌ヨ閮ㄩ棬涓嬫媺鏍戠粨鏋�
+    getDeptOptions() {
+      return new Promise((resolve, reject) => {
+        if (!this.deptOptions || this.deptOptions.length <= 0) {
+          // 閫夋嫨閮ㄩ棬鏍� 鏁版嵁绗竴娆¤姹�
+          getDeptTree().then(response => {
+            this.deptOptions = [].concat(
+              [{ id: '', label: '鍏ㄩ儴', children: [] }],
+              [...response.data]
+            );
+            this.handleNodeClick(this.deptOptions[0])
+            resolve()
+          })
+        } else {
+          // 纭繚閫夋嫨閮ㄩ棬鏍戝凡鍒濆鍖� 浣嗕粛璇锋眰琛ㄦ牸鏁版嵁 澶嶉�夋鍥炴樉
+          this.handleNodeClick(this.deptOptions[0])
+        }
+      });
+    },
+    // 鏌ヨ閮ㄩ棬涓嬫媺鏍戠粨鏋勶紙鍚儴闂ㄥ墠缂�锛�
+    getDeptTreeData() {
+      function refactorTree(data) {
+        return data.map(node => {
+          let treeData = { id: `DEPT${node.id}`, label: node.label, parentId: node.parentId, weight: node.weight };
+          if (node.children && node.children.length > 0) {
+            treeData.children = refactorTree(node.children);
+          }
+          return treeData;
+        });
+      }
+      return new Promise((resolve, reject) => {
+        if (!this.deptTreeData || this.deptTreeData.length <= 0) {
+          getDeptTree().then((res) => {
+            this.deptTreeData = refactorTree(res.data);
+            resolve()
+          }).catch(() => {
+            reject()
+          })
+        } else {
+          resolve()
+        }
+      })
+    },
+    // 鏌ヨ閮ㄩ棬涓嬫媺鏍戠粨鏋�
+    getRoleOptions() {
+      if (!this.roleOptions || this.roleOptions.length <= 0) {
+        roleList().then(response => this.roleOptions = response.rows);
+      }
+    },
+    // 鏌ヨ鐢ㄦ埛鍒楄〃
+    getList() {
+      this.tableLoading = true
+      userList({ ...this.queryParams }).then(response => {
+        this.userTableList = response.rows;
+        this.userTotal = response.total;
+       // this.$nextTick(() => {
+         // setTimeout(() => {
+         //   this.selectedUserDate.forEach(each => {
+          //    const findObj = this.userTableList.find(find => find.userId === each.userId) || {}
+          //    if(Object.keys(findObj).length > 0) {
+         //       this.$refs.multipleTable.toggleRowSelection(findObj)
+         //     }
+        //    })
+        //  }, 500)
+       // })
+      }).finally(() => {
+        this.tableLoading = false
+      })
+    },
+    /** 鎼滅储鎸夐挳鎿嶄綔 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 閲嶇疆鎸夐挳鎿嶄綔 */
+    resetQuery() {
+      this.$refs.queryForm.resetFields();
+      this.$refs.queryForm.clearValidate();
+      this.queryParams = {
+        deptId: '',
+        nickName: undefined,
+        pageSize: 20
+      };
+      this.handleQuery();
+    },
+    // 绛涢�夎妭鐐�
+    filterNode(value, data) {
+      if (!value) return true;
+      return data.label.indexOf(value) !== -1;
+    },
+    // 鑺傜偣鍗曞嚮浜嬩欢
+    handleNodeClick(data) {
+      this.queryParams.deptId = data.id;
+      this.handleQuery();
+    },
+    // 鍏抽棴鏍囩
+    handleClose(tag) {
+      this.selectedUserDate.splice(this.selectedUserDate.indexOf(tag), 1);
+      this.handleTaskUserChange()
+    },
+    /**
+     * 閫夋嫨琛�
+     */
+    clickRow(row) {
+      this.$refs.multipleTable.toggleRowSelection(row);
+    },
+    // 澶氶�夋閫変腑鏁版嵁
+    handleSelectionChange(selection) {
+      if(selection.length) {
+        const concatData = [].concat(selection, this.selectedUserDate);
+        this.selectedUserDate = uniqBy(concatData, 'userId');
+      }
+    },
+    // 娣诲姞鐢ㄦ埛
+    onSelectUsers() {
+      this.$nextTick(()=>
+       {
+          setTimeout(() => {
+            this.$refs.multipleTable.clearSelection(),100
+          })
+      })
+      this.getDeptOptions()
+      this.userOpen = true;
+
+    },
+    // 娣诲姞鐢ㄦ埛-纭鎸夐挳
+    handleTaskUserComplete() {
+      if (!this.selectedUserDate || this.selectedUserDate.length <= 0) {
+        this.$modal.msgError('璇烽�夋嫨鐢ㄦ埛');
+        return;
+      }
+      USER_TASK_FORM.dataType = USER_TASK_GROUP[0].value;
+      this.handleTaskUserChange()
+      this.changeMultiLoopType();
+      this.userOpen = false;
+    },
+    // 鎸囧畾鐢ㄦ埛鏀瑰彉纭畾
+    handleTaskUserChange() {
+      this.selectedUser.text = this.selectedUserDate.map(item => item.nickName) || [];
+      if (this.selectedUserDate.length === 1) {
+        let data = this.selectedUserDate[0];
+        USER_TASK_FORM.assignee = data.userId;
+        USER_TASK_FORM.text = data.nickName;
+        USER_TASK_FORM.candidateUsers = null;
+        this.showMultiFlog = false;
+        this.multiLoopType = MULTI_INSTANCE_TYPE[0].value;
+      } else {
+        USER_TASK_FORM.candidateUsers = this.selectedUserDate.map(item => item.userId).join() || null;
+        USER_TASK_FORM.text = this.selectedUserDate.map(item => item.nickName).join() || null;
+        USER_TASK_FORM.assignee = null;
+        this.showMultiFlog = true;
+      }
+      this.selectedUserDate=[];
+      this.updateElementTask();
+    },
+    // 閫変腑瑙掕壊
+    changeSelectRoles(roleArray) {
+      let groups = null;
+      let text = null;
+      if (roleArray && roleArray.length > 0) {
+        USER_TASK_FORM.dataType = USER_TASK_GROUP[1].value;
+        groups = roleArray.join() || null;
+        let textArr = this.roleOptions.filter(k => roleArray.indexOf(`ROLE${k.roleId}`) >= 0);
+        text = textArr?.map(k => k.roleName).join() || null;
+      } else {
+        USER_TASK_FORM.dataType = null;
+        this.multiLoopType = MULTI_INSTANCE_TYPE[0].value;
+      }
+      USER_TASK_FORM.candidateGroups = groups;
+      USER_TASK_FORM.text = text;
+      this.updateElementTask();
+      this.changeMultiLoopType();
+    },
+
+
+    checkedDeptChangeNode(node){
+     this.checkedDeptChange([node.id]);
+    },
+    clearDept(){
+     USER_TASK_FORM.candidateGroups = "";
+     USER_TASK_FORM.text = "";
+     USER_TASK_FORM.assignee ="";
+     USER_TASK_FORM.candidateUsers ="";
+     this.updateElementTask();
+     this.multiLoopType = MULTI_INSTANCE_TYPE[0].value;
+     this.changeMultiLoopType();
+    },
+    // 閫変腑閮ㄩ棬
+    checkedDeptChange(deptArray) {
+      let groups = null;
+      let text = null;
+      this.deptIds = deptArray[0];
+      if (deptArray && deptArray.length > 0) {
+        USER_TASK_FORM.dataType = USER_TASK_GROUP[2].value;
+        groups = deptArray.join(',') || null;
+        let treeStarkData = JSON.parse(JSON.stringify(this.deptTreeData));
+        const filterData = this.filterTreeData(treeStarkData, deptArray);
+        text = filterData.map(item => item.label).join(',') || null
+      } else {
+        USER_TASK_FORM.dataType = null;
+        this.multiLoopType = MULTI_INSTANCE_TYPE[0].value;
+      }
+      USER_TASK_FORM.candidateGroups = groups;
+      USER_TASK_FORM.text = text;
+      this.updateElementTask();
+      this.changeMultiLoopType();
+    },
+   filterTreeData  (data, deptArray)  {
+      const result = [];
+      const traverse = (items) => {
+        for (const item of items) {
+          // 妫�鏌ュ綋鍓嶈妭鐐规槸鍚﹀湪 deptArray 涓�
+          if (deptArray.includes(item.id)) {
+            result.push(item); // 濡傛灉鍦紝娣诲姞鍒扮粨鏋滄暟缁勪腑
+          }
+
+          // 濡傛灉鏈� children锛岄�掑綊澶勭悊
+          if (item.children) {
+            traverse(item.children);
+          }
+        }
+      };
+      traverse(data); // 寮�濮嬮亶鍘�
+      return result; // 杩斿洖鎵佸钩鍖栫殑缁撴灉鏁扮粍
+  },
+
+    // 淇敼瀹℃壒浜虹被鍨�
+    changeDataType(val) {
+      if (val === USER_TASK_GROUP[1].value || val === USER_TASK_GROUP[2].value || (val === USER_TASK_GROUP[0].value && this.selectedUser.ids.length > 1)) {
+        this.showMultiFlog = true;
+      } else {
+        this.showMultiFlog = false;
+      }
+      this.multiLoopType = MULTI_INSTANCE_TYPE[0].value;
+      this.changeMultiLoopType();
+      // 娓呯┖ USER_TASK_FORM 鎵�鏈夊睘鎬у��
+      Object.keys(USER_TASK_FORM).forEach(key => USER_TASK_FORM[key] = null);
+      USER_TASK_FORM.dataType = val;
+      if (val === USER_TASK_GROUP[0].value) {
+        if (this.selectedUser && this.selectedUser.ids && this.selectedUser.ids.length > 0) {
+          if (this.selectedUser.ids.length === 1) {
+            USER_TASK_FORM.assignee = this.selectedUser.ids[0];
+          } else {
+            USER_TASK_FORM.candidateUsers = this.selectedUser.ids.join()
+          }
+          USER_TASK_FORM.text = this.selectedUser.text?.join() || null
+        }
+      } else if (val === USER_TASK_GROUP[1].value) {
+        this.getRoleOptions();
+        if (this.roleIds && this.roleIds.length > 0) {
+          USER_TASK_FORM.candidateGroups = this.roleIds.join() || null;
+          let textArr = this.roleOptions.filter(k => this.roleIds.indexOf(`ROLE${k.roleId}`) >= 0);
+          USER_TASK_FORM.text = textArr?.map(k => k.roleName).join() || null;
+        }
+      } else if (val === USER_TASK_GROUP[2].value) {
+        this.getDeptTreeData();
+        if (this.deptIds && this.deptIds.length > 0) {
+          USER_TASK_FORM.candidateGroups = this.deptIds.join() || null;
+          let textArr = []
+          let treeStarkData = JSON.parse(JSON.stringify(this.deptTreeData));
+          this.deptIds.forEach(id => {
+            let stark = []
+            stark = stark.concat(treeStarkData);
+            while(stark.length) {
+              let temp = stark.shift();
+              if(temp.children) {
+                stark = temp.children.concat(stark);
+              }
+              if(id === temp.id) {
+                textArr.push(temp);
+              }
+            }
+          })
+          USER_TASK_FORM.text = textArr?.map(k => k.label).join() || null;
+        }
+      } else {
+        USER_TASK_FORM.assignee = "${initiator}";
+        USER_TASK_FORM.text = "娴佺▼鍙戣捣浜�";
+      }
+      this.updateElementTask();
+    },
+    // 鍒濆鍖栧瀹炰緥鍙橀噺
+    getElementLoop(businessObject) {
+      if (!businessObject.loopCharacteristics) {
+        this.multiLoopType = MULTI_INSTANCE_TYPE[0].value;
+        return;
+      }
+      this.isSequential = businessObject.loopCharacteristics.isSequential;
+      if (businessObject.loopCharacteristics.completionCondition) {
+        if (businessObject.loopCharacteristics.completionCondition.body === "${nrOfCompletedInstances >= nrOfInstances}") {
+          this.multiLoopType = MULTI_INSTANCE_TYPE[1].value; // 浼氱
+        } else if (businessObject.loopCharacteristics.completionCondition.body === "${nrOfCompletedInstances >= 0}") {
+          this.multiLoopType = MULTI_INSTANCE_TYPE[2].value; // 鎴栫
+        } else {
+          this.multiLoopType = MULTI_INSTANCE_TYPE[3].value; // 鑷畾涔�
+        }
+      }
+    },
+    // 鏇存柊澶氬疄渚嬪彉閲�
+    changeMultiLoopType() {
+      // 鍙栨秷澶氬疄渚嬮厤缃�
+      if (this.multiLoopType === MULTI_INSTANCE_TYPE[0].value) {
+        window.bpmnInstances.modeling.updateProperties(this.bpmnElement, { loopCharacteristics: null, candidateUsers: null });
+        return;
+      }
+
+      this.multiLoopInstance = window.bpmnInstances.moddle.create("bpmn:MultiInstanceLoopCharacteristics", { isSequential: this.isSequential });
+      // 鏇存柊澶氬疄渚嬮厤缃�
+      window.bpmnInstances.modeling.updateProperties(this.bpmnElement, {
+        loopCharacteristics: this.multiLoopInstance,
+        assignee: '${assignee}'
+      });
+      // 璁剧疆瀹℃壒浜�
+      USER_TASK_FORM.assignee = '${assignee}';
+      // 瀹屾垚鏉′欢
+      let completionCondition = null;
+      switch (this.multiLoopType) {
+        case MULTI_INSTANCE_TYPE[1].value:
+          completionCondition = window.bpmnInstances.moddle.create("bpmn:FormalExpression", { body: "${nrOfCompletedInstances >= nrOfInstances}" });
+          break;
+        case MULTI_INSTANCE_TYPE[2].value:
+          completionCondition = window.bpmnInstances.moddle.create("bpmn:FormalExpression", { body: "${nrOfCompletedInstances > 0}" });
+          break;
+        default:
+          completionCondition = window.bpmnInstances.moddle.create("bpmn:FormalExpression", { body: "" });
+          break;
+      }
+      // 鏇存柊妯″潡灞炴�т俊鎭�
+      window.bpmnInstances.modeling.updateModdleProperties(this.bpmnElement, this.multiLoopInstance, {
+        collection: MULTI_INSTANCE_TYPE[1].value || MULTI_INSTANCE_TYPE[2].value ? '${multiInstanceHandler.getUserIds(execution)}' : "",
+        elementVariable: MULTI_INSTANCE_TYPE[1].value || MULTI_INSTANCE_TYPE[2].value ? 'assignee' : "",
+        completionCondition
+      });
+    },
+  },
+  beforeDestroy() {
+    this.bpmnElement = null;
+  },
+};
+</script>
+<style scoped lang="scss">
+.el-row .el-radio-group {
+  margin-bottom: 15px;
+  .el-radio {
+    line-height: 28px;
+  }
+}
+.el-tag {
+  margin-bottom: 10px;
+  + .el-tag {
+    margin-left: 10px;
+  }
+}
+
+.custom-label {
+  padding-left: 5px;
+  font-weight: 500;
+  font-size: 14px;
+  color: #606266;
+}
+
+.head-container {
+  height: 480px;
+  overflow-y: auto;
+}
+.el-row .radio-group-flex {
+  margin: 15px 0 0 0;
+  justify-items: flex-start;
+  align-items: flex-start !important;
+}
+</style>
+
diff --git a/ruoyi-ui/apps/web-antd/src/package/theme/element-variables.scss b/ruoyi-ui/apps/web-antd/src/package/theme/element-variables.scss
new file mode 100644
index 0000000..02f74f0
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/theme/element-variables.scss
@@ -0,0 +1,70 @@
+/* 鏀瑰彉涓婚鑹插彉閲� */
+$--color-primary: #1890ff;
+$--color-danger: #ff4d4f;
+
+/* 鏀瑰彉 icon 瀛椾綋璺緞鍙橀噺锛屽繀闇� */
+// $--font-path: '~element-ui/lib/theme-chalk/fonts';
+
+// @import "~element-ui/packages/theme-chalk/src/index";
+
+.el-table td,
+.el-table th {
+  color: #333;
+}
+.el-drawer__header {
+  padding: 16px 16px 8px 16px;
+  margin: 0;
+  line-height: 24px;
+  font-size: 18px;
+  color: #303133;
+  box-sizing: border-box;
+  border-bottom: 1px solid #e8e8e8;
+}
+div[class^="el-drawer"]:focus,
+span:focus {
+  outline: none;
+}
+.el-drawer__body {
+  box-sizing: border-box;
+  padding: 16px;
+  width: 100%;
+  overflow-y: auto;
+}
+
+.el-dialog {
+  margin-top: 50vh !important;
+  transform: translateY(-50%);
+  overflow: hidden;
+}
+.el-dialog__wrapper {
+  overflow: hidden;
+  max-height: 100vh;
+}
+.el-dialog__header {
+  padding: 16px 16px 8px 16px;
+  box-sizing: border-box;
+  border-bottom: 1px solid #e8e8e8;
+}
+.el-dialog__body {
+  padding: 16px;
+  max-height: 80vh;
+  box-sizing: border-box;
+  overflow-y: auto;
+}
+.el-dialog__footer {
+  padding: 16px;
+  box-sizing: border-box;
+  border-top: 1px solid #e8e8e8;
+}
+.el-dialog__close {
+  font-weight: 600;
+}
+.el-select {
+  width: 100%;
+}
+.el-divider:not(.el-divider--horizontal) {
+  margin: 0 8px ;
+}
+.el-divider.el-divider--horizontal {
+  margin: 16px 0;
+}
diff --git a/ruoyi-ui/apps/web-antd/src/package/theme/index.scss b/ruoyi-ui/apps/web-antd/src/package/theme/index.scss
new file mode 100644
index 0000000..93e7c09
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/theme/index.scss
@@ -0,0 +1,168 @@
+@use "bpmn-js-token-simulation/assets/css/bpmn-js-token-simulation.css";
+@use "bpmn-js/dist/assets/diagram-js.css";
+@use "bpmn-js/dist/assets/bpmn-font/css/bpmn.css";
+@use "bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css";
+@use "./process-designer.scss";
+@use "./process-panel.scss";
+
+$success-color: #4eb819;
+$primary-color: #409EFF;
+$warning-color: #E6A23C;
+$danger-color: #F56C6C;
+$cancel-color: #909399;
+
+.process-viewer {
+  position: relative;
+  border: 1px solid #EFEFEF;
+  background: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PHBhdHRlcm4gaWQ9ImEiIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgcGF0dGVyblVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHBhdGggZD0iTTAgMTBoNDBNMTAgMHY0ME0wIDIwaDQwTTIwIDB2NDBNMCAzMGg0ME0zMCAwdjQwIiBmaWxsPSJub25lIiBzdHJva2U9IiNlMGUwZTAiIG9wYWNpdHk9Ii4yIi8+PHBhdGggZD0iTTQwIDBIMHY0MCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZTBlMGUwIi8+PC9wYXR0ZXJuPjwvZGVmcz48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2EpIi8+PC9zdmc+') repeat!important;
+
+  .success-arrow {
+    fill: $success-color;
+    stroke: $success-color;
+  }
+
+  .success-conditional {
+    fill: white;
+    stroke: $success-color;
+  }
+
+  .fail-arrow {
+    fill: $warning-color;
+    stroke: $warning-color;
+  }
+
+  .fail-conditional {
+    fill: white;
+    stroke: $warning-color;
+  }
+
+  .success.djs-connection {
+    .djs-visual path {
+      stroke: $success-color!important;
+      marker-end: url(#sequenceflow-end-white-success)!important;
+    }
+  }
+
+  .success.djs-connection.condition-expression {
+    .djs-visual path {
+      marker-start: url(#conditional-flow-marker-white-success)!important;
+    }
+  }
+
+  .success.djs-shape {
+    .djs-visual rect {
+      stroke: $success-color!important;
+      fill: $success-color!important;
+      fill-opacity: 0.15!important;
+    }
+
+    .djs-visual polygon {
+      stroke: $success-color!important;
+    }
+
+    .djs-visual path:nth-child(2) {
+      stroke: $success-color!important;
+      fill: $success-color!important;
+    }
+
+    .djs-visual circle {
+      stroke: $success-color!important;
+      fill: $success-color!important;
+      fill-opacity: 0.15!important;
+    }
+  }
+
+  .primary.djs-shape {
+    .djs-visual rect {
+      stroke: $primary-color!important;
+      fill: $primary-color!important;
+      fill-opacity: 0.15!important;
+    }
+
+    .djs-visual polygon {
+      stroke: $primary-color!important;
+    }
+
+    .djs-visual circle {
+      stroke: $primary-color!important;
+      fill: $primary-color!important;
+      fill-opacity: 0.15!important;
+    }
+  }
+
+  .warning.djs-connection {
+    .djs-visual path {
+      stroke: $warning-color!important;
+      marker-end: url(#sequenceflow-end-white-fail)!important;
+    }
+  }
+
+  .warning.djs-connection.condition-expression {
+    .djs-visual path {
+      marker-start: url(#conditional-flow-marker-white-fail)!important;
+    }
+  }
+
+  .warning.djs-shape {
+    .djs-visual rect {
+      stroke: $warning-color!important;
+      fill: $warning-color!important;
+      fill-opacity: 0.15!important;
+    }
+
+    .djs-visual polygon {
+      stroke: $warning-color!important;
+    }
+
+    .djs-visual path:nth-child(2) {
+      stroke: $warning-color!important;
+      fill: $warning-color!important;
+    }
+
+    .djs-visual circle {
+      stroke: $warning-color!important;
+      fill: $warning-color!important;
+      fill-opacity: 0.15!important;
+    }
+  }
+
+  .danger.djs-shape {
+    .djs-visual rect {
+      stroke: $danger-color!important;
+      fill: $danger-color!important;
+      fill-opacity: 0.15!important;
+    }
+
+    .djs-visual polygon {
+      stroke: $danger-color!important;
+    }
+
+    .djs-visual circle {
+      stroke: $danger-color!important;
+      fill: $danger-color!important;
+      fill-opacity: 0.15!important;
+    }
+  }
+
+  .cancel.djs-shape {
+    .djs-visual rect {
+      stroke: $cancel-color!important;
+      fill: $cancel-color!important;
+      fill-opacity: 0.15!important;
+    }
+
+    .djs-visual polygon {
+      stroke: $cancel-color!important;
+    }
+
+    .djs-visual circle {
+      stroke: $cancel-color!important;
+      fill: $cancel-color!important;
+      fill-opacity: 0.15!important;
+    }
+  }
+}
+
+.process-viewer .djs-tooltip-container, .process-viewer .djs-overlay-container, .process-viewer .djs-palette {
+  display: none;
+}
diff --git a/ruoyi-ui/apps/web-antd/src/package/theme/process-designer.scss b/ruoyi-ui/apps/web-antd/src/package/theme/process-designer.scss
new file mode 100644
index 0000000..386a153
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/theme/process-designer.scss
@@ -0,0 +1,155 @@
+@import "bpmn-js-token-simulation/assets/css/bpmn-js-token-simulation.css";
+@import "diagram-js-minimap/assets/diagram-js-minimap.css";
+
+// 杈规琚� token-simulation 鏍峰紡瑕嗙洊浜�
+.djs-palette {
+  background: var(--palette-background-color);
+  border: solid 1px var(--palette-border-color) !important;
+  border-radius: 2px;
+}
+
+.my-process-designer {
+  display: flex;
+  flex-direction: column;
+  width: 100%;
+  height: 100%;
+  box-sizing: border-box;
+  .my-process-designer__header {
+    width: 100%;
+    min-height: 36px;
+    .el-button {
+      text-align: center;
+    }
+    .el-button-group {
+      margin: 4px;
+    }
+    .el-tooltip__popper {
+      .el-button {
+        width: 100%;
+        text-align: left;
+        padding-left: 8px;
+        padding-right: 8px;
+      }
+      .el-button:hover {
+        background: rgba(64, 158, 255, 0.8);
+        color: #ffffff;
+      }
+    }
+    .align {
+      position: relative;
+      i {
+        &:after {
+          content: "|";
+          position: absolute;
+          transform: rotate(90deg) translate(200%, -10%);
+        }
+      }
+    }
+    .align.align-left i {
+      transform: rotate(90deg);
+    }
+    .align.align-right i {
+      transform: rotate(-90deg);
+    }
+    .align.align-top i {
+      transform: rotate(180deg);
+    }
+    .align.align-bottom i {
+      transform: rotate(0deg);
+    }
+    .align.align-center i {
+      transform: rotate(90deg);
+      &:after {
+        transform: rotate(90deg) translate(0, -10%);
+      }
+    }
+    .align.align-middle i {
+      transform: rotate(0deg);
+      &:after {
+        transform: rotate(90deg) translate(0, -10%);
+      }
+    }
+  }
+  .my-process-designer__container {
+    display: inline-flex;
+    width: 100%;
+    flex: 1;
+    .my-process-designer__canvas {
+      flex: 1;
+      height: 100%;
+      position: relative;
+      background: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PHBhdHRlcm4gaWQ9ImEiIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgcGF0dGVyblVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHBhdGggZD0iTTAgMTBoNDBNMTAgMHY0ME0wIDIwaDQwTTIwIDB2NDBNMCAzMGg0ME0zMCAwdjQwIiBmaWxsPSJub25lIiBzdHJva2U9IiNlMGUwZTAiIG9wYWNpdHk9Ii4yIi8+PHBhdGggZD0iTTQwIDBIMHY0MCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZTBlMGUwIi8+PC9wYXR0ZXJuPjwvZGVmcz48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2EpIi8+PC9zdmc+")
+      repeat !important;
+      div.toggle-mode {
+        display: none;
+      }
+    }
+    .my-process-designer__property-panel {
+      height: 100%;
+      overflow: scroll;
+      overflow-y: auto;
+      z-index: 10;
+      * {
+        box-sizing: border-box;
+      }
+    }
+    svg {
+      width: 100%;
+      height: 100%;
+      min-height: 100%;
+      overflow: hidden;
+    }
+  }
+}
+
+//渚ц竟鏍忛厤缃�
+.djs-palette.open {
+  .djs-palette-entries {
+    div[class^="bpmn-icon-"]:before,
+    div[class*="bpmn-icon-"]:before {
+      line-height: unset;
+    }
+    div.entry {
+      position: relative;
+    }
+    div.entry:hover {
+      &::after {
+        width: max-content;
+        content: attr(title);
+        vertical-align: text-bottom;
+        position: absolute;
+        right: -10px;
+        top: 0;
+        bottom: 0;
+        overflow: hidden;
+        transform: translateX(100%);
+        font-size: 0.5em;
+        display: inline-block;
+        text-decoration: inherit;
+        font-variant: normal;
+        text-transform: none;
+        background: #fafafa;
+        box-shadow: 0 0 6px #eeeeee;
+        border: 1px solid #cccccc;
+        box-sizing: border-box;
+        padding: 0 16px;
+        border-radius: 4px;
+        z-index: 100;
+      }
+    }
+  }
+}
+pre {
+  margin: 0;
+  height: 100%;
+  overflow: hidden;
+  max-height: calc(80vh - 32px);
+  overflow-y: auto;
+}
+.hljs {
+  word-break: break-word;
+  white-space: pre-wrap;
+}
+.hljs * {
+  font-family: Consolas, Monaco, monospace;
+}
diff --git a/ruoyi-ui/apps/web-antd/src/package/theme/process-panel.scss b/ruoyi-ui/apps/web-antd/src/package/theme/process-panel.scss
new file mode 100644
index 0000000..8d72afb
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/theme/process-panel.scss
@@ -0,0 +1,129 @@
+.process-panel__container {
+  box-sizing: border-box;
+  padding: 0 8px;
+  border-left: 1px solid #eeeeee;
+  box-shadow: 0 0 8px #cccccc;
+  max-height: 100%;
+  overflow-y: scroll;
+}
+.panel-tab__title {
+  font-weight: 600;
+  padding: 0 8px;
+  font-size: 1.1em;
+  line-height: 1.2em;
+  i {
+    margin-right: 8px;
+    font-size: 1.2em;
+  }
+}
+.panel-tab__content {
+  width: 100%;
+  box-sizing: border-box;
+  border-top: 1px solid #eeeeee;
+  padding: 8px 16px;
+  .panel-tab__content--title {
+    display: flex;
+    justify-content: space-between;
+    padding-bottom: 8px;
+    span {
+      flex: 1;
+      text-align: left;
+    }
+  }
+}
+.element-property {
+  width: 100%;
+  display: flex;
+  align-items: flex-start;
+  margin: 8px 0;
+  .element-property__label {
+    display: block;
+    width: 90px;
+    text-align: right;
+    overflow: hidden;
+    padding-right: 12px;
+    line-height: 32px;
+    font-size: 14px;
+    box-sizing: border-box;
+  }
+  .element-property__value {
+    flex: 1;
+    line-height: 32px;
+  }
+  .el-form-item {
+    width: 100%;
+    margin-bottom: 0;
+    padding-bottom: 18px;
+  }
+}
+.list-property {
+  flex-direction: column;
+  .element-listener-item {
+    width: 100%;
+    display: inline-grid;
+    grid-template-columns: 16px auto 32px 32px;
+    grid-column-gap: 8px;
+  }
+  .element-listener-item + .element-listener-item {
+    margin-top: 8px;
+  }
+}
+.listener-drawer {
+
+  .el-drawer__header {
+    margin: 0PX;
+    padding: 10PX;
+    border-bottom: 1px solid #eeeeee;
+    .el-drawer__title {
+      font-size: 18px
+    }
+  }
+
+  .el-drawer__body {
+    .el-form-item__label {
+      font-size: 14PX;
+      font-weight: 500;
+    }
+    .input-with-select .el-input-group__prepend {
+      background-color: var(--el-fill-color-blank);
+      padding: 0;
+    }
+  }
+}
+.listener-filed__title {
+  display: inline-flex;
+  width: 100%;
+  justify-content: space-between;
+  align-items: center;
+  margin-top: 0;
+  span {
+    // width: 200px;
+    text-align: left;
+    font-size: 14px;
+  }
+  i {
+    margin-right: 8px;
+  }
+}
+.element-drawer__button {
+  margin-top: 8px;
+  width: 100%;
+  display: inline-flex;
+  justify-content: space-around;
+}
+.element-drawer__button > .el-button {
+  width: 100%;
+}
+
+.el-collapse-item__content {
+  padding-bottom: 0;
+}
+.el-input.is-disabled .el-input__inner {
+  color: #999999;
+}
+.el-form-item.el-form-item--mini {
+  margin-bottom: 0;
+  & + .el-form-item {
+    margin-top: 16px;
+  }
+}
diff --git a/ruoyi-ui/apps/web-antd/src/package/utils.js b/ruoyi-ui/apps/web-antd/src/package/utils.js
new file mode 100644
index 0000000..bd61ef6
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/package/utils.js
@@ -0,0 +1,71 @@
+// 鍒涘缓鐩戝惉鍣ㄥ疄渚�
+export function createListenerObject(options, isTask, prefix) {
+  const listenerObj = Object.create(null);
+  listenerObj.event = options.event;
+  isTask && (listenerObj.id = options.id); // 浠诲姟鐩戝惉鍣ㄧ壒鏈夌殑 id 瀛楁
+  switch (options.listenerType) {
+    case 'scriptListener':
+      listenerObj.script = createScriptObject(options, prefix);
+      break;
+    case 'expressionListener':
+      listenerObj.expression = options.expression;
+      break;
+    case 'delegateExpressionListener':
+      listenerObj.delegateExpression = options.delegateExpression;
+      break;
+    default:
+      listenerObj.class = options.class;
+  }
+  // 娉ㄥ叆瀛楁
+  if (options.fields) {
+    listenerObj.fields = options.fields.map(field => {
+      return createFieldObject(field, prefix);
+    });
+  }
+  // 浠诲姟鐩戝惉鍣ㄧ殑 瀹氭椂鍣� 璁剧疆
+  if (isTask && options.event === 'timeout' && !!options.eventDefinitionType) {
+    const timeDefinition = window.bpmnInstances.moddle.create('bpmn:FormalExpression', {
+      body: options.eventTimeDefinitions
+    });
+    const TimerEventDefinition = window.bpmnInstances.moddle.create('bpmn:TimerEventDefinition', {
+      id: `TimerEventDefinition_${uuid(8)}`,
+      [`time${options.eventDefinitionType.replace(/^\S/, s => s.toUpperCase())}`]: timeDefinition
+    });
+    listenerObj.eventDefinitions = [TimerEventDefinition];
+  }
+  return window.bpmnInstances.moddle.create(`${prefix}:${isTask ? 'TaskListener' : 'ExecutionListener'}`, listenerObj);
+}
+
+// 鍒涘缓 鐩戝惉鍣ㄧ殑娉ㄥ叆瀛楁 瀹炰緥
+export function createFieldObject(option, prefix) {
+  const { name, fieldType, string, expression } = option;
+  const fieldConfig = fieldType === 'string' ? { name, string } : { name, expression };
+  return window.bpmnInstances.moddle.create(`${prefix}:Field`, fieldConfig);
+}
+
+// 鍒涘缓鑴氭湰瀹炰緥
+export function createScriptObject(options, prefix) {
+  const { scriptType, scriptFormat, value, resource } = options;
+  const scriptConfig = scriptType === 'inlineScript' ? { scriptFormat, value } : { scriptFormat, resource };
+  return window.bpmnInstances.moddle.create(`${prefix}:Script`, scriptConfig);
+}
+
+// 鏇存柊鍏冪礌鎵╁睍灞炴��
+export function updateElementExtensions(element, extensionList) {
+  const extensions = window.bpmnInstances.moddle.create('bpmn:ExtensionElements', {
+    values: extensionList
+  });
+  window.bpmnInstances.modeling.updateProperties(element, {
+    extensionElements: extensions
+  });
+}
+
+// 鍒涘缓涓�涓猧d
+export function uuid(length = 8, chars) {
+  let result = '';
+  const charsString = chars || '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
+  for (let i = length; i > 0; --i) {
+    result += charsString[Math.floor(Math.random() * charsString.length)];
+  }
+  return result;
+}
diff --git a/ruoyi-ui/apps/web-antd/src/utils/min-dash.js b/ruoyi-ui/apps/web-antd/src/utils/min-dash.js
new file mode 100644
index 0000000..ab8b4e0
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/utils/min-dash.js
@@ -0,0 +1,694 @@
+/* eslint-disable no-func-assign */
+/**
+ * Flatten array, one level deep.
+ *
+ * @param {Array<?>} arr
+ *
+ * @return {Array<?>}
+ */
+function flatten(arr) {
+  return Array.prototype.concat.apply([], arr);
+}
+
+var nativeToString = Object.prototype.toString;
+var nativeHasOwnProperty = Object.prototype.hasOwnProperty;
+function isUndefined(obj) {
+  return obj === undefined;
+}
+function isDefined(obj) {
+  return obj !== undefined;
+}
+function isNil(obj) {
+  return obj == null;
+}
+function isArray(obj) {
+  return nativeToString.call(obj) === '[object Array]';
+}
+function isObject(obj) {
+  return nativeToString.call(obj) === '[object Object]';
+}
+function isNumber(obj) {
+  return nativeToString.call(obj) === '[object Number]';
+}
+function isFunction(obj) {
+  var tag = nativeToString.call(obj);
+  return tag === '[object Function]' || tag === '[object AsyncFunction]' || tag === '[object GeneratorFunction]' || tag === '[object AsyncGeneratorFunction]' || tag === '[object Proxy]';
+}
+function isString(obj) {
+  return nativeToString.call(obj) === '[object String]';
+}
+/**
+ * Ensure collection is an array.
+ *
+ * @param {Object} obj
+ */
+
+function ensureArray(obj) {
+  if (isArray(obj)) {
+    return;
+  }
+
+  throw new Error('must supply array');
+}
+/**
+ * Return true, if target owns a property with the given key.
+ *
+ * @param {Object} target
+ * @param {String} key
+ *
+ * @return {Boolean}
+ */
+
+function has(target, key) {
+  return nativeHasOwnProperty.call(target, key);
+}
+
+/**
+ * Find element in collection.
+ *
+ * @param  {Array|Object} collection
+ * @param  {Function|Object} matcher
+ *
+ * @return {Object}
+ */
+
+function find(collection, matcher) {
+  matcher = toMatcher(matcher);
+  var match;
+  forEach(collection, function(val, key) {
+    if (matcher(val, key)) {
+      match = val;
+      return false;
+    }
+  });
+  return match;
+}
+/**
+ * Find element index in collection.
+ *
+ * @param  {Array|Object} collection
+ * @param  {Function} matcher
+ *
+ * @return {Object}
+ */
+
+function findIndex(collection, matcher) {
+  matcher = toMatcher(matcher);
+  var idx = isArray(collection) ? -1 : undefined;
+  forEach(collection, function(val, key) {
+    if (matcher(val, key)) {
+      idx = key;
+      return false;
+    }
+  });
+  return idx;
+}
+/**
+ * Find element in collection.
+ *
+ * @param  {Array|Object} collection
+ * @param  {Function} matcher
+ *
+ * @return {Array} result
+ */
+
+function filter(collection, matcher) {
+  var result = [];
+  forEach(collection, function(val, key) {
+    if (matcher(val, key)) {
+      result.push(val);
+    }
+  });
+  return result;
+}
+/**
+ * Iterate over collection; returning something
+ * (non-undefined) will stop iteration.
+ *
+ * @param  {Array|Object} collection
+ * @param  {Function} iterator
+ *
+ * @return {Object} return result that stopped the iteration
+ */
+
+function forEach(collection, iterator) {
+  var val, result;
+
+  if (isUndefined(collection)) {
+    return;
+  }
+
+  var convertKey = isArray(collection) ? toNum : identity;
+
+  for (var key in collection) {
+    if (has(collection, key)) {
+      val = collection[key];
+      result = iterator(val, convertKey(key));
+
+      if (result === false) {
+        return val;
+      }
+    }
+  }
+}
+/**
+ * Return collection without element.
+ *
+ * @param  {Array} arr
+ * @param  {Function} matcher
+ *
+ * @return {Array}
+ */
+
+function without(arr, matcher) {
+  if (isUndefined(arr)) {
+    return [];
+  }
+
+  ensureArray(arr);
+  matcher = toMatcher(matcher);
+  return arr.filter(function(el, idx) {
+    return !matcher(el, idx);
+  });
+}
+/**
+ * Reduce collection, returning a single result.
+ *
+ * @param  {Object|Array} collection
+ * @param  {Function} iterator
+ * @param  {Any} result
+ *
+ * @return {Any} result returned from last iterator
+ */
+
+function reduce(collection, iterator, result) {
+  forEach(collection, function(value, idx) {
+    result = iterator(result, value, idx);
+  });
+  return result;
+}
+/**
+ * Return true if every element in the collection
+ * matches the criteria.
+ *
+ * @param  {Object|Array} collection
+ * @param  {Function} matcher
+ *
+ * @return {Boolean}
+ */
+
+function every(collection, matcher) {
+  return !!reduce(collection, function(matches, val, key) {
+    return matches && matcher(val, key);
+  }, true);
+}
+/**
+ * Return true if some elements in the collection
+ * match the criteria.
+ *
+ * @param  {Object|Array} collection
+ * @param  {Function} matcher
+ *
+ * @return {Boolean}
+ */
+
+function some(collection, matcher) {
+  return !!find(collection, matcher);
+}
+/**
+ * Transform a collection into another collection
+ * by piping each member through the given fn.
+ *
+ * @param  {Object|Array}   collection
+ * @param  {Function} fn
+ *
+ * @return {Array} transformed collection
+ */
+
+function map(collection, fn) {
+  var result = [];
+  forEach(collection, function(val, key) {
+    result.push(fn(val, key));
+  });
+  return result;
+}
+/**
+ * Get the collections keys.
+ *
+ * @param  {Object|Array} collection
+ *
+ * @return {Array}
+ */
+
+function keys(collection) {
+  return collection && Object.keys(collection) || [];
+}
+/**
+ * Shorthand for `keys(o).length`.
+ *
+ * @param  {Object|Array} collection
+ *
+ * @return {Number}
+ */
+
+function size(collection) {
+  return keys(collection).length;
+}
+/**
+ * Get the values in the collection.
+ *
+ * @param  {Object|Array} collection
+ *
+ * @return {Array}
+ */
+
+function values(collection) {
+  return map(collection, function(val) {
+    return val;
+  });
+}
+/**
+ * Group collection members by attribute.
+ *
+ * @param  {Object|Array} collection
+ * @param  {Function} extractor
+ *
+ * @return {Object} map with { attrValue => [ a, b, c ] }
+ */
+
+function groupBy(collection, extractor) {
+  var grouped = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
+  extractor = toExtractor(extractor);
+  forEach(collection, function(val) {
+    var discriminator = extractor(val) || '_';
+    var group = grouped[discriminator];
+
+    if (!group) {
+      group = grouped[discriminator] = [];
+    }
+
+    group.push(val);
+  });
+  return grouped;
+}
+function uniqueBy(extractor) {
+  extractor = toExtractor(extractor);
+  var grouped = {};
+
+  for (var _len = arguments.length, collections = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
+    collections[_key - 1] = arguments[_key];
+  }
+
+  forEach(collections, function(c) {
+    return groupBy(c, extractor, grouped);
+  });
+  var result = map(grouped, function(val, key) {
+    return val[0];
+  });
+  return result;
+}
+var unionBy = uniqueBy;
+/**
+ * Sort collection by criteria.
+ *
+ * @param  {Object|Array} collection
+ * @param  {String|Function} extractor
+ *
+ * @return {Array}
+ */
+
+function sortBy(collection, extractor) {
+  extractor = toExtractor(extractor);
+  var sorted = [];
+  forEach(collection, function(value, key) {
+    var disc = extractor(value, key);
+    var entry = {
+      d: disc,
+      v: value
+    };
+
+    for (var idx = 0; idx < sorted.length; idx++) {
+      var d = sorted[idx].d;
+
+      if (disc < d) {
+        sorted.splice(idx, 0, entry);
+        return;
+      }
+    } // not inserted, append (!)
+
+    sorted.push(entry);
+  });
+  return map(sorted, function(e) {
+    return e.v;
+  });
+}
+/**
+ * Create an object pattern matcher.
+ *
+ * @example
+ *
+ * const matcher = matchPattern({ id: 1 });
+ *
+ * let element = find(elements, matcher);
+ *
+ * @param  {Object} pattern
+ *
+ * @return {Function} matcherFn
+ */
+
+function matchPattern(pattern) {
+  return function(el) {
+    return every(pattern, function(val, key) {
+      return el[key] === val;
+    });
+  };
+}
+
+function toExtractor(extractor) {
+  return isFunction(extractor) ? extractor : function(e) {
+    return e[extractor];
+  };
+}
+
+function toMatcher(matcher) {
+  return isFunction(matcher) ? matcher : function(e) {
+    return e === matcher;
+  };
+}
+
+function identity(arg) {
+  return arg;
+}
+
+function toNum(arg) {
+  return Number(arg);
+}
+
+/**
+ * Debounce fn, calling it only once if the given time
+ * elapsed between calls.
+ *
+ * Lodash-style the function exposes methods to `#clear`
+ * and `#flush` to control internal behavior.
+ *
+ * @param  {Function} fn
+ * @param  {Number} timeout
+ *
+ * @return {Function} debounced function
+ */
+function debounce(fn, timeout) {
+  var timer;
+  var lastArgs;
+  var lastThis;
+  var lastNow;
+
+  function fire(force) {
+    var now = Date.now();
+    var scheduledDiff = force ? 0 : lastNow + timeout - now;
+
+    if (scheduledDiff > 0) {
+      return schedule(scheduledDiff);
+    }
+
+    fn.apply(lastThis, lastArgs);
+    clear();
+  }
+
+  function schedule(timeout) {
+    timer = setTimeout(fire, timeout);
+  }
+
+  function clear() {
+    if (timer) {
+      clearTimeout(timer);
+    }
+
+    timer = lastNow = lastArgs = lastThis = undefined;
+  }
+
+  function flush() {
+    if (timer) {
+      fire(true);
+    }
+
+    clear();
+  }
+
+  function callback() {
+    lastNow = Date.now();
+
+    for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
+      args[_key] = arguments[_key];
+    }
+
+    lastArgs = args;
+    lastThis = this; // ensure an execution is scheduled
+
+    if (!timer) {
+      schedule(timeout);
+    }
+  }
+
+  callback.flush = flush;
+  callback.cancel = clear;
+  return callback;
+}
+/**
+ * Throttle fn, calling at most once
+ * in the given interval.
+ *
+ * @param  {Function} fn
+ * @param  {Number} interval
+ *
+ * @return {Function} throttled function
+ */
+
+function throttle(fn, interval) {
+  var throttling = false;
+  return function() {
+    if (throttling) {
+      return;
+    }
+
+    fn.apply(void 0, arguments);
+    throttling = true;
+    setTimeout(function() {
+      throttling = false;
+    }, interval);
+  };
+}
+/**
+ * Bind function against target <this>.
+ *
+ * @param  {Function} fn
+ * @param  {Object}   target
+ *
+ * @return {Function} bound function
+ */
+
+function bind(fn, target) {
+  return fn.bind(target);
+}
+
+function _typeof(obj) {
+  '@babel/helpers - typeof';
+
+  if (typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol') {
+    _typeof = function(obj) {
+      return typeof obj;
+    };
+  } else {
+    _typeof = function(obj) {
+      return obj && typeof Symbol === 'function' && obj.constructor === Symbol && obj !== Symbol.prototype ? 'symbol' : typeof obj;
+    };
+  }
+
+  return _typeof(obj);
+}
+
+function _extends() {
+  _extends = Object.assign || function(target) {
+    for (var i = 1; i < arguments.length; i++) {
+      var source = arguments[i];
+
+      for (var key in source) {
+        if (Object.prototype.hasOwnProperty.call(source, key)) {
+          target[key] = source[key];
+        }
+      }
+    }
+
+    return target;
+  };
+
+  return _extends.apply(this, arguments);
+}
+
+/**
+ * Convenience wrapper for `Object.assign`.
+ *
+ * @param {Object} target
+ * @param {...Object} others
+ *
+ * @return {Object} the target
+ */
+
+function assign(target) {
+  for (var _len = arguments.length, others = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
+    others[_key - 1] = arguments[_key];
+  }
+
+  return _extends.apply(void 0, [target].concat(others));
+}
+/**
+ * Sets a nested property of a given object to the specified value.
+ *
+ * This mutates the object and returns it.
+ *
+ * @param {Object} target The target of the set operation.
+ * @param {(string|number)[]} path The path to the nested value.
+ * @param {any} value The value to set.
+ */
+
+function set(target, path, value) {
+  var currentTarget = target;
+  forEach(path, function(key, idx) {
+    if (typeof key !== 'number' && typeof key !== 'string') {
+      throw new Error('illegal key type: ' + _typeof(key) + '. Key should be of type number or string.');
+    }
+
+    if (key === 'constructor') {
+      throw new Error('illegal key: constructor');
+    }
+
+    if (key === '__proto__') {
+      throw new Error('illegal key: __proto__');
+    }
+
+    var nextKey = path[idx + 1];
+    var nextTarget = currentTarget[key];
+
+    if (isDefined(nextKey) && isNil(nextTarget)) {
+      nextTarget = currentTarget[key] = isNaN(+nextKey) ? {} : [];
+    }
+
+    if (isUndefined(nextKey)) {
+      if (isUndefined(value)) {
+        delete currentTarget[key];
+      } else {
+        currentTarget[key] = value;
+      }
+    } else {
+      currentTarget = nextTarget;
+    }
+  });
+  return target;
+}
+/**
+ * Gets a nested property of a given object.
+ *
+ * @param {Object} target The target of the get operation.
+ * @param {(string|number)[]} path The path to the nested value.
+ * @param {any} [defaultValue] The value to return if no value exists.
+ */
+
+function get(target, path, defaultValue) {
+  var currentTarget = target;
+  forEach(path, function(key) {
+    // accessing nil property yields <undefined>
+    if (isNil(currentTarget)) {
+      currentTarget = undefined;
+      return false;
+    }
+
+    currentTarget = currentTarget[key];
+  });
+  return isUndefined(currentTarget) ? defaultValue : currentTarget;
+}
+/**
+ * Pick given properties from the target object.
+ *
+ * @param {Object} target
+ * @param {Array} properties
+ *
+ * @return {Object} target
+ */
+
+function pick(target, properties) {
+  var result = {};
+  var obj = Object(target);
+  forEach(properties, function(prop) {
+    if (prop in obj) {
+      result[prop] = target[prop];
+    }
+  });
+  return result;
+}
+/**
+ * Pick all target properties, excluding the given ones.
+ *
+ * @param {Object} target
+ * @param {Array} properties
+ *
+ * @return {Object} target
+ */
+
+function omit(target, properties) {
+  var result = {};
+  var obj = Object(target);
+  forEach(obj, function(prop, key) {
+    if (properties.indexOf(key) === -1) {
+      result[key] = prop;
+    }
+  });
+  return result;
+}
+/**
+ * Recursively merge `...sources` into given target.
+ *
+ * Does support merging objects; does not support merging arrays.
+ *
+ * @param {Object} target
+ * @param {...Object} sources
+ *
+ * @return {Object} the target
+ */
+
+function merge(target) {
+  for (var _len2 = arguments.length, sources = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
+    sources[_key2 - 1] = arguments[_key2];
+  }
+
+  if (!sources.length) {
+    return target;
+  }
+
+  forEach(sources, function(source) {
+    // skip non-obj sources, i.e. null
+    if (!source || !isObject(source)) {
+      return;
+    }
+
+    forEach(source, function(sourceVal, key) {
+      if (key === '__proto__') {
+        return;
+      }
+
+      var targetVal = target[key];
+
+      if (isObject(sourceVal)) {
+        if (!isObject(targetVal)) {
+          // override target[key] with object
+          targetVal = {};
+        }
+
+        target[key] = merge(targetVal, sourceVal);
+      } else {
+        target[key] = sourceVal;
+      }
+    });
+  });
+  return target;
+}
+
+export { assign, bind, debounce, ensureArray, every, filter, find, findIndex, flatten, forEach, get, groupBy, has, isArray, isDefined, isFunction, isNil, isNumber, isObject, isString, isUndefined, keys, map, matchPattern, merge, omit, pick, reduce, set, size, some, sortBy, throttle, unionBy, uniqueBy, values, without };
diff --git a/ruoyi-ui/apps/web-antd/src/views/workflow/category/index.vue b/ruoyi-ui/apps/web-antd/src/views/workflow/category/index.vue
index 31b29f4..ac75df5 100644
--- a/ruoyi-ui/apps/web-antd/src/views/workflow/category/index.vue
+++ b/ruoyi-ui/apps/web-antd/src/views/workflow/category/index.vue
@@ -1,156 +1,203 @@
-<script setup lang="ts">
-import type { VbenFormProps } from '@vben/common-ui';
-import type { Recordable } from '@vben/types';
+<template>
+ <div class="app-container">
+      <div class="search" v-show="showSearch">
+        <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
+          <el-form-item label="鍒嗙被鍚嶇О" prop="categoryName">
+            <el-input v-model="queryParams.categoryName" placeholder="璇疯緭鍏ュ垎绫诲悕绉�" clearable @keyup.enter="handleQuery" />
+          </el-form-item>
+          <el-form-item label="鍒嗙被缂栫爜" prop="code">
+            <el-input v-model="queryParams.code" placeholder="璇疯緭鍏ュ垎绫荤紪鐮�" clearable @keyup.enter="handleQuery" />
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
+            <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+          </el-form-item>
+        </el-form>
+      </div>
+    <el-card shadow="never">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+          <el-col :span="1.5">
+            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['workflow:category:add']">鏂板</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['workflow:category:edit']">
+              淇敼
+            </el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['workflow:category:remove']">
+              鍒犻櫎
+            </el-button>
+          </el-col>
+          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+        </el-row>
+      </template>
 
-import type { VxeGridProps } from '#/adapter/vxe-table';
+      <el-table v-loading="loading" :data="categoryList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="鍒嗙被缂栧彿" align="center" prop="categoryId" v-if="true" />
+        <el-table-column label="鍒嗙被鍚嶇О" align="center" prop="categoryName" />
+        <el-table-column label="鍒嗙被缂栫爜" align="center" prop="code" />
+        <el-table-column label="澶囨敞" align="center" prop="remark" />
+        <el-table-column label="鎿嶄綔" align="center" width="150" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-tooltip content="淇敼" placement="top">
+              <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['workflow:category:edit']"></el-button>
+            </el-tooltip>
+            <el-tooltip content="鍒犻櫎" placement="top">
+              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['workflow:category:remove']"></el-button>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+      </el-table>
+      <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+    </el-card>
 
-import { nextTick } from 'vue';
+    <!-- 娣诲姞鎴栦慨鏀瑰弬鏁伴厤缃璇濇 -->
+    <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+      <el-form ref="categoryFormRef" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="鍒嗙被鍚嶇О" prop="categoryName">
+          <el-input v-model="form.categoryName" placeholder="璇疯緭鍏ュ垎绫诲悕绉�" />
+        </el-form-item>
+        <el-form-item label="鍒嗙被缂栫爜" prop="code">
+          <el-input v-model="form.code" placeholder="璇疯緭鍏ュ垎绫荤紪鐮�" />
+        </el-form-item>
+        <el-form-item label="澶囨敞" prop="remark">
+          <el-input v-model="form.remark" type="textarea" placeholder="璇疯緭鍏ュ唴瀹�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="submitForm">纭� 瀹�</el-button>
+          <el-button @click="cancel">鍙� 娑�</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
 
-import { Page, useVbenModal } from '@vben/common-ui';
-import { getVxePopupContainer } from '@vben/utils';
+<script setup name="Category" >
+import { listCategory, getCategory, delCategory, addCategory, updateCategory } from "#/api/workflow/category";
 
-import { Popconfirm, Space } from 'ant-design-vue';
+const { proxy } = getCurrentInstance();
 
-import { useVbenVxeGrid } from '#/adapter/vxe-table';
-import { categoryList, categoryRemove } from '#/api/workflow/category';
+const categoryList = ref([]);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
 
-import categoryModal from './category-modal.vue';
-import { columns, querySchema } from './data';
-
-const formOptions: VbenFormProps = {
-  commonConfig: {
-    labelWidth: 80,
-    componentProps: {
-      allowClear: true,
-    },
+const queryFormRef = ref();
+const categoryFormRef = ref();
+const dialog = reactive({
+  visible: false,
+  title: ''
+});
+const initFormData = {
+  categoryId: undefined,
+  categoryName: '',
+  code: '',
+  remark: ''
+}
+const data = reactive({
+  form: {...initFormData},
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    categoryName: '',
+    code: ''
   },
-  schema: querySchema(),
-  wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4',
-};
-
-const gridOptions: VxeGridProps = {
-  columns,
-  height: 'auto',
-  keepSource: true,
-  pagerConfig: {
-    enabled: false,
-  },
-  proxyConfig: {
-    ajax: {
-      query: async (_, formValues = {}) => {
-        const resp = await categoryList({
-          ...formValues,
-        });
-        return { rows: resp };
-      },
-      // 榛樿璇锋眰鎺ュ彛鍚庡睍寮�鍏ㄩ儴 涓嶉渶瑕佸彲浠ュ垹闄よ繖娈�
-      querySuccess: () => {
-        nextTick(() => {
-          expandAll();
-        });
-      },
-    },
-  },
-  /**
-   * 铏氭嫙婊氬姩  榛樿鍏抽棴
-   */
-  scrollY: {
-    enabled: false,
-    gt: 0,
-  },
-  rowConfig: {
-    keyField: 'categoryId',
-  },
-  treeConfig: {
-    parentField: 'parentId',
-    rowField: 'categoryId',
-    transform: true,
-  },
-  // 琛ㄦ牸鍏ㄥ眬鍞竴琛ㄧず 淇濆瓨鍒楅厤缃渶瑕佺敤鍒�
-  id: 'workflow-category-index',
-};
-
-const [BasicTable, tableApi] = useVbenVxeGrid({ formOptions, gridOptions });
-const [CategoryModal, modalApi] = useVbenModal({
-  connectedComponent: categoryModal,
+  rules: {
+    categoryName: [{ required: true, message: "鍒嗙被鍚嶇О涓嶈兘涓虹┖", trigger: "blur" }],
+    code: [{ required: true, message: "鍒嗙被缂栫爜涓嶈兘涓虹┖", trigger: "blur" }]
+  }
 });
 
-function handleAdd(row?: Recordable<any>) {
-  modalApi.setData({ parentId: row?.categoryId });
-  modalApi.open();
+const { queryParams, form, rules } = toRefs(data);
+
+/** 鏌ヨ鍙傛暟鍒楄〃 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listCategory(queryParams.value);
+  categoryList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+}
+/** 鍙栨秷鎸夐挳 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+}
+/** 琛ㄥ崟閲嶇疆 */
+const reset = () => {
+  form.value = {...initFormData};
+  categoryFormRef.value.resetFields();
+}
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+}
+/** 閲嶇疆鎸夐挳鎿嶄綔 */
+const resetQuery = () => {
+  queryFormRef.value.resetFields();
+  handleQuery();
+}
+/** 澶氶�夋閫変腑鏁版嵁 */
+const handleSelectionChange = (selection) => {
+  ids.value = selection.map(item => item.categoryId);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+}
+/** 鏂板鎸夐挳鎿嶄綔 */
+const handleAdd = () => {
+  dialog.visible = true;
+  dialog.title = "娣诲姞鍙傛暟";
+  nextTick(() => {
+    reset();
+  })
+}
+/** 淇敼鎸夐挳鎿嶄綔 */
+const handleUpdate = (row) => {
+  dialog.visible = true;
+  dialog.title = "淇敼鍙傛暟";
+  const categoryId = row?.categoryId || ids.value[0];
+  nextTick(async () => {
+    reset();
+    const res = await getCategory(categoryId);
+    form.value = res.data;
+  })
+}
+/** 鎻愪氦鎸夐挳 */
+const submitForm = () => {
+  categoryFormRef.value.validate(async (valid) => {
+    if (valid) {
+      form.value.categoryId ? await updateCategory(form.value) : await addCategory(form.value);
+      proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+      dialog.visible = false;
+      getList();
+    }
+  });
+}
+/** 鍒犻櫎鎸夐挳鎿嶄綔 */
+const handleDelete = async (row) => {
+  const categoryIds = row?.categoryId || ids.value;
+  await proxy?.$modal.confirm('鏄惁纭鍒犻櫎鍙傛暟缂栧彿涓�"' + categoryIds + '"鐨勬暟鎹」锛�');
+  await delCategory(categoryIds);
+  getList();
+  proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+}
+/** 瀵煎嚭鎸夐挳鎿嶄綔 */
+const handleExport = () => {
+  proxy?.download("workflow/category/export", {
+    ...queryParams.value
+  }, `category_${new Date().getTime()}.xlsx`);
 }
 
-async function handleEdit(row: Recordable<any>) {
-  modalApi.setData({ id: row.categoryId });
-  modalApi.open();
-}
-
-async function handleDelete(row: Recordable<any>) {
-  await categoryRemove(row.categoryId);
-  await tableApi.query();
-}
-
-function expandAll() {
-  tableApi.grid?.setAllTreeExpand(true);
-}
-
-function collapseAll() {
-  tableApi.grid?.setAllTreeExpand(false);
-}
+onMounted(() => {
+  getList();
+})
 </script>
-
-<template>
-  <Page :auto-content-height="true">
-    <BasicTable table-title="娴佺▼鍒嗙被鍒楄〃">
-      <template #toolbar-tools>
-        <Space>
-          <a-button @click="collapseAll">
-            {{ $t('pages.common.collapse') }}
-          </a-button>
-          <a-button @click="expandAll">
-            {{ $t('pages.common.expand') }}
-          </a-button>
-          <a-button
-            type="primary"
-            v-access:code="['workflow:category:add']"
-            @click="handleAdd"
-          >
-            {{ $t('pages.common.add') }}
-          </a-button>
-        </Space>
-      </template>
-      <template #action="{ row }">
-        <Space>
-          <ghost-button
-            v-access:code="['workflow:category:edit']"
-            @click.stop="handleEdit(row)"
-          >
-            {{ $t('pages.common.edit') }}
-          </ghost-button>
-          <ghost-button
-            class="btn-success"
-            v-access:code="['workflow:category:edit']"
-            @click.stop="handleAdd(row)"
-          >
-            {{ $t('pages.common.add') }}
-          </ghost-button>
-          <Popconfirm
-            :get-popup-container="getVxePopupContainer"
-            placement="left"
-            title="纭鍒犻櫎锛�"
-            @confirm="handleDelete(row)"
-          >
-            <ghost-button
-              danger
-              v-access:code="['workflow:category:remove']"
-              @click.stop=""
-            >
-              {{ $t('pages.common.delete') }}
-            </ghost-button>
-          </Popconfirm>
-        </Space>
-      </template>
-    </BasicTable>
-    <CategoryModal @reload="tableApi.query()" />
-  </Page>
-</template>
diff --git a/ruoyi-ui/apps/web-antd/src/views/workflow/deploy/index.vue b/ruoyi-ui/apps/web-antd/src/views/workflow/deploy/index.vue
new file mode 100644
index 0000000..0ff3fe1
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/views/workflow/deploy/index.vue
@@ -0,0 +1,236 @@
+<template>
+ <div class="app-container">
+      <div class="search" v-show="showSearch">
+        <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
+          <el-form-item label="娴佺▼鏍囪瘑" prop="processKey">
+            <el-input v-model="queryParams.processKey" placeholder="璇疯緭鍏ユ祦绋嬫爣璇�" clearable @keyup.enter="handleQuery" />
+          </el-form-item>
+          <el-form-item label="娴佺▼鍚嶇О" prop="processName">
+            <el-input v-model="queryParams.processName" placeholder="璇疯緭鍏ユ祦绋嬪悕绉�" clearable @keyup.enter="handleQuery" />
+          </el-form-item>
+          <el-form-item label="娴佺▼鍒嗙被" prop="category">
+            <el-select v-model="queryParams.category" clearable placeholder="娴佺▼鍒嗙被"  style="width: 240px">
+              <el-option v-for="item in categoryOptions" :key="item.categoryId" :label="item.categoryName" :value="item.code" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="鐘舵��" prop="state">
+            <el-select v-model="queryParams.state" clearable placeholder="璇烽�夋嫨鐘舵��"  style="width: 240px">
+              <el-option :key="1" label="婵�娲�" value="active" />
+              <el-option :key="2" label="鎸傝捣" value="suspended" />
+            </el-select>
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
+            <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+          </el-form-item>
+        </el-form>
+      </div>
+    <el-card shadow="never">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+          <el-col :span="1.5">
+            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['workflow:deploy:remove']">
+              鍒犻櫎
+            </el-button>
+          </el-col>
+          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" fit :data="deployList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="娴佺▼鏍囪瘑" align="center" prop="processKey" :show-overflow-tooltip="true" />
+        <el-table-column label="娴佺▼鍚嶇О" align="center" prop="processName" :show-overflow-tooltip="true" />
+        <el-table-column label="娴佺▼鍒嗙被" align="center" prop="categoryName" :formatter="categoryFormat" />
+        <el-table-column label="娴佺▼鐗堟湰" align="center">
+          <template #default="scope">
+            <el-tag size="small">v{{ scope.row.version }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="鐘舵��" align="center">
+          <template #default="scope">
+            <el-tag type="success" v-if="!scope.row.suspended">婵�娲�</el-tag>
+            <el-tag type="warning" v-if="scope.row.suspended">鎸傝捣</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="閮ㄧ讲鏃堕棿" align="center" prop="deploymentTime" width="180" >
+          <template #default="scope">
+              {{  parseTime(scope.row.deploymentTime, '{y}-{m}-{d}  {h}:{i}:{s}')}}
+          </template>
+        </el-table-column>
+        <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-tooltip content="鐗堟湰绠$悊" placement="top">
+              <el-button link type="primary" icon="PriceTag" @click="handlePublish(scope.row)" v-hasPermi="['workflow:deploy:list']"></el-button>
+            </el-tooltip>
+            <el-tooltip content="鍒犻櫎" placement="top">
+              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['workflow:deploy:remove']"></el-button>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+      </el-table>
+      <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+    </el-card>
+
+<!--    &lt;!&ndash; 娴佺▼鍥惧璇濇 &ndash;&gt;-->
+<!--    <el-dialog :title="processDialog.title" v-model="processDialog.visible" width="500px" append-to-body>-->
+<!--      <process-viewer :key="`designer-${processView.index}`" :xml="processView.xmlData" :style="{height: '400px'}" />-->
+<!--    </el-dialog>-->
+
+    <!-- 鐗堟湰绠$悊 -->
+    <el-dialog title="鐗堟湰绠$悊" v-model="publishDialog.visible" width="700px" append-to-body>
+      <el-table v-loading="publishLoading" :data="publishList">
+        <el-table-column label="娴佺▼鏍囪瘑" align="center" prop="processKey" :show-overflow-tooltip="true" />
+        <el-table-column label="娴佺▼鍚嶇О" align="center" :show-overflow-tooltip="true">
+          <template #default="scope">
+            <a class="link-type"  @click="handleProcessView(scope.row)">
+              <span>{{ scope.row.processName }}</span>
+            </a>
+          </template>
+        </el-table-column>
+        <el-table-column label="娴佺▼鐗堟湰" align="center">
+          <template #default="scope">
+            <el-tag size="small">v{{ scope.row.version }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="鐘舵��" align="center">
+          <template #default="scope">
+            <el-tag type="success" v-if="!scope.row.suspended">婵�娲�</el-tag>
+            <el-tag type="warning" v-if="scope.row.suspended">鎸傝捣</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-tooltip content="鎸傝捣" placement="top">
+              <el-button link type="primary" icon="VideoPause" @click="handleChangeState(scope.row, 'suspended')" v-hasPermi="['workflow:deploy:status']"></el-button>
+            </el-tooltip>
+            <el-tooltip content="婵�娲�" placement="top">
+              <el-button link type="primary" icon="VideoPlay" @click="handleChangeState(scope.row, 'active')" v-hasPermi="['workflow:deploy:status']"></el-button>
+            </el-tooltip>
+            <el-tooltip content="鍒犻櫎" placement="top">
+              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['workflow:deploy:remove']"></el-button>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+      </el-table>
+      <pagination v-show="publishTotal > 0" :total="publishTotal" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getPublishList" />
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="Deploy" lang="js">
+import { listAllCategory } from '#/api/workflow/category'
+import { listDeploy, listPublish, getBpmnXml, changeState, delDeploy } from '#/api/workflow/deploy'
+import { ElForm } from 'element-plus';
+
+const { proxy } = getCurrentInstance() ;
+
+const categoryOptions = ref([]);
+
+const deployList = ref([]);
+const loading = ref(true);
+const showSearch = ref(true)
+const ids = ref([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+const publishList = ref([]);
+const publishLoading = ref(true);
+const publishTotal = ref(0);
+
+const queryFormRef = ref();
+const processDialog = reactive({
+  visible: false,
+  title: ''
+});
+const publishDialog = reactive({
+  visible: false,
+  title: ''
+});
+const data = reactive({
+  form: {},
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    processKey: '',
+    processName: '',
+    category: ''
+  },
+  rules: {}
+});
+const { queryParams } = toRefs(data);
+
+/** 鏌ヨ娴佺▼鍒嗙被鍒楄〃 */
+const getCategoryList = async () => {
+  const res = await listAllCategory();
+  categoryOptions.value = res.data;
+};
+
+const getList = async () => {
+  loading.value = true;
+  const res = await listDeploy(queryParams.value);
+  deployList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+}
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+}
+/** 閲嶇疆鎸夐挳鎿嶄綔 */
+const resetQuery = () => {
+  queryFormRef.value.resetFields();
+  handleQuery();
+}
+/** 澶氶�夋閫変腑鏁版嵁 */
+const handleSelectionChange = (selection) => {
+  ids.value = selection.map(item => item.deploymentId);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+}
+/** 鍒犻櫎鎸夐挳鎿嶄綔 */
+const handleDelete = async (row) => {
+  const deploymentIds = row.deploymentId || ids.value;
+  await proxy.$modal.confirm('鏄惁纭鍒犻櫎鍙傛暟缂栧彿涓�"' + deploymentIds + '"鐨勬暟鎹」锛�');
+  await delDeploy(deploymentIds);
+  getList();
+  proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+}
+const getPublishList = async () => {
+  publishLoading.value = true;
+  const res = await listPublish(queryParams.value);
+  publishList.value = res.rows;
+  publishTotal.value = res.total;
+  publishLoading.value = false;
+}
+const handlePublish = (row) => {
+  queryParams.value.processKey = row.processKey;
+  publishDialog.visible = true;
+  getPublishList();
+}
+const handleProcessView = (row) => {
+
+}
+const handleChangeState = async (row, state) => {
+  const params = {
+    definitionId: row.definitionId,
+    state: state
+  }
+  await changeState(params);
+  proxy.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+  getPublishList();
+}
+
+const categoryFormat = (row) => {
+  var category = categoryOptions.value.find(function(k) {
+        return k.code === row.category;
+    });
+    return category ? category.categoryName : '';
+}
+
+onMounted(() => {
+  getCategoryList();
+  getList();
+})
+</script>
diff --git a/ruoyi-ui/apps/web-antd/src/views/workflow/form/index.vue b/ruoyi-ui/apps/web-antd/src/views/workflow/form/index.vue
new file mode 100644
index 0000000..75aeab5
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/views/workflow/form/index.vue
@@ -0,0 +1,251 @@
+<template>
+ <div class="app-container">
+      <div class="search" v-show="showSearch">
+        <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="70">
+          <el-form-item label="琛ㄥ崟鍚嶇О" prop="formName">
+            <el-input v-model="queryParams.formName" placeholder="璇疯緭鍏ヨ〃鍗曞悕绉�" clearable @keyup.enter="handleQuery" />
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
+            <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+          </el-form-item>
+        </el-form>
+      </div>
+    <el-card shadow="never">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+          <el-col :span="1.5">
+            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['workflow:form:add']">鏂板</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['workflow:form:edit']">淇敼</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['workflow:form:remove']">鍒犻櫎</el-button>
+          </el-col>
+          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" :data="formList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="琛ㄥ崟涓婚敭" align="center" prop="formId" v-if="false" />
+        <el-table-column label="琛ㄥ崟鍚嶇О" align="center" prop="formName" />
+        <el-table-column label="澶囨敞" align="center" prop="remark" />
+        <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-tooltip content="璇︽儏" placement="top">
+              <el-button link type="primary" icon="View" @click="handleDetail(scope.row)" v-hasPermi="['workflow:form:query']"></el-button>
+            </el-tooltip>
+            <el-tooltip content="淇敼" placement="top">
+              <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['workflow:form:edit']"></el-button>
+            </el-tooltip>
+            <el-tooltip content="鍒犻櫎" placement="top">
+              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['workflow:form:remove']"></el-button>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+      </el-table>
+      <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+    </el-card>
+
+    <!-- 娴佺▼琛ㄥ崟璁捐鍣ㄥ璇濇 -->
+    <el-dialog :title="designer.title" v-model="designer.visible" fullscreen>
+      <div id="form-designer">
+        <v-form-designer ref="vfDesignerRef" :resetFormJson="true" :designer-config="designerConfig">
+          <!-- 鑷畾涔夋寜閽彃妲� -->
+          <template #customToolButtons>
+            <el-button link type="primary" icon="Finished" @click="dialog.visible = true">淇濆瓨</el-button>
+          </template>
+        </v-form-designer>
+      </div>
+    </el-dialog>
+
+    <!-- 棰勮琛ㄥ崟瀵硅瘽妗� -->
+    <el-dialog :title="render.title" v-model="render.visible" width="60%" append-to-body>
+      <v-form-render :form-json="{}" :form-data="{}" ref="vfRenderRef" />
+    </el-dialog>
+
+    <!-- 娣诲姞鎴栦慨鏀规祦绋嬭〃鍗曞璇濇 -->
+    <el-dialog :title="dialog.title" v-model="dialog.visible" width="600px" append-to-body>
+      <el-form ref="formFormRef" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="琛ㄥ崟鍚嶇О" prop="formName">
+          <el-input v-model="form.formName" placeholder="璇疯緭鍏ヨ〃鍗曞悕绉�" />
+        </el-form-item>
+        <el-form-item label="澶囨敞" prop="remark">
+          <el-input v-model="form.remark" placeholder="璇疯緭鍏ュ娉�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="submitForm">纭� 瀹�</el-button>
+          <el-button @click="cancel">鍙� 娑�</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="Form" >
+import { listForm, addForm, updateForm, getForm, delForm } from "#/api/workflow/form";
+
+const { proxy } = getCurrentInstance();
+
+const formList = ref([]);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+
+const vfDesignerRef = ref(null);
+const vfRenderRef = ref(null);
+const formFormRef = ref();
+const queryFormRef = ref();
+
+const designerConfig = reactive({
+  externalLink: true,
+  toolbarMaxWidth: 510,
+  // languageMenu: true,
+  //externalLink: false,
+  //formTemplates: false,
+  //eventCollapse: false,
+  //clearDesignerButton: false,
+  //previewFormButton: false,
+})
+const designer = reactive({
+  visible: false,
+  title: ''
+})
+const render = reactive({
+  visible: false,
+  title: ''
+})
+const dialog = reactive({
+  visible: false,
+  title: ''
+});
+const initFormData = {
+  formId: undefined,
+  formName: '',
+  content: '',
+  remark: ''
+}
+const data = reactive({
+  form: {...initFormData},
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    formName: ''
+  },
+  rules: {
+    formName: [{ required: true, message: "琛ㄥ崟鍚嶇О涓嶈兘涓虹┖", trigger: "blur" }]
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 鏌ヨ宀椾綅鍒楄〃 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listForm(queryParams.value);
+  formList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+}
+/** 鍙栨秷鎸夐挳 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+}
+/** 琛ㄥ崟閲嶇疆 */
+const reset = () => {
+  form.value = {...initFormData};
+}
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+}
+/** 閲嶇疆鎸夐挳鎿嶄綔 */
+const resetQuery = () => {
+  queryFormRef.value.resetFields();
+  handleQuery();
+}
+/** 澶氶�夋閫変腑鏁版嵁 */
+const handleSelectionChange = (selection) => {
+  ids.value = selection.map(item => item.formId);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+}
+/** 鏂板琛ㄥ崟鎿嶄綔 */
+const handleAdd = () => {
+  designer.visible = true;
+  nextTick(() => {
+    reset();
+    vfDesignerRef.value.clearDesigner();
+  })
+}
+/** 淇敼琛ㄥ崟鎿嶄綔 */
+const handleUpdate = (row) => {
+  designer.visible = true;
+  nextTick(async () => {
+    const formId = row?.formId || ids.value[0];
+    const res = await getForm(formId);
+    form.value = res.data;
+    vfDesignerRef.value.setFormJson(form.value.content);
+  })
+}
+/** 鏌ョ湅琛ㄥ崟鎿嶄綔 */
+const handleDetail = (row) => {
+  render.visible = true;
+  render.title = '鏌ョ湅琛ㄥ崟璇︽儏';
+  nextTick(async () => {
+    vfRenderRef.value.setFormJson(row.content || {formConfig: {}, widgetList: []});
+  });
+}
+/** 鎻愪氦琛ㄥ崟鎿嶄綔 */
+const submitForm = () => {
+  const formJson = vfDesignerRef.value.getFormJson();
+  form.value.content = JSON.stringify(formJson);
+  nextTick(async () => {
+    form.value.formId ? await updateForm(form.value) : await addForm(form.value);
+    proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+    dialog.visible = false;
+    designer.visible = false;
+    getList();
+  })
+}
+/** 鍒犻櫎鎸夐挳鎿嶄綔 */
+const handleDelete = async (row) => {
+  const formIds = row?.formId || ids.value;
+  await proxy?.$modal.confirm('鏄惁纭鍒犻櫎琛ㄥ崟缂栧彿涓�"' + formIds + '"鐨勬暟鎹」锛�');
+  await delForm(formIds);
+  getList();
+  proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+}
+/** 瀵煎嚭鎸夐挳鎿嶄綔 */
+const handleExport = () => {
+  proxy?.download("workflow/form/export", {
+    ...queryParams.value
+  }, `form_${new Date().getTime()}.xlsx`);
+}
+
+getList();
+</script>
+
+<style lang="scss" scoped>
+#form-designer {
+  .main-container {
+    margin: 0;
+  }
+  label {
+    font-weight: normal;
+  }
+  :deep(.external-link) {
+    display: flex;
+    align-items: center;
+  }
+}
+</style>
diff --git a/ruoyi-ui/apps/web-antd/src/views/workflow/listener/index.vue b/ruoyi-ui/apps/web-antd/src/views/workflow/listener/index.vue
new file mode 100644
index 0000000..5a89532
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/views/workflow/listener/index.vue
@@ -0,0 +1,469 @@
+<template>
+ <div class="app-container">
+    <!-- 鏌ヨ鏉′欢 -->
+      <div v-show="showSearch" class="search">
+        <el-form ref="queryFormRef" :inline="true" :model="queryParams" label-width="120">
+          <el-form-item label="鐩戝惉绫诲瀷" prop="listenerType">
+            <el-select v-model="queryParams.listenerType" clearable placeholder="璇烽�夋嫨鐩戝惉绫诲瀷" @change="handleQuery">
+              <el-option v-for="dict in sys_listener_type" :key="dict.value" :label="dict.label" :value="dict.value" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="鍚嶇О" prop="name">
+            <el-input v-model="queryParams.name" clearable placeholder="璇疯緭鍏ュ悕绉�" @keyup.enter="handleQuery" />
+          </el-form-item>
+          <el-form-item label="浜嬩欢绫诲瀷" prop="eventType">
+            <el-select v-model="queryParams.eventType" clearable placeholder="璇烽�夋嫨浜嬩欢绫诲瀷" @change="handleQuery">
+              <el-option
+                v-for="dict in sys_task_listener_event_type"
+                v-if="queryParams.listenerType === 'task'"
+                :key="dict.value"
+                :label="dict.label"
+                :value="dict.value"
+              />
+              <el-option
+                v-for="dict in sys_execution_listener_event_type"
+                v-if="queryParams.listenerType === 'execution'"
+                :key="dict.value"
+                :label="dict.label"
+                :value="dict.value"
+              />
+              <el-option
+                v-for="dict in sys_task_listener_event_type"
+                v-if="!queryParams.listenerType"
+                :key="dict.value"
+                :label="dict.label"
+                :value="dict.value"
+              />
+              <el-option
+                v-for="dict in sys_execution_listener_event_type"
+                v-if="!queryParams.listenerType"
+                :key="dict.value"
+                :label="dict.label"
+                :value="dict.value"
+              />
+            </el-select>
+          </el-form-item>
+          <el-form-item>
+            <el-button icon="Search" type="primary" @click="handleQuery">鎼滅储</el-button>
+            <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+          </el-form-item>
+        </el-form>
+      </div>
+
+    <el-card shadow="never">
+      <template #header>
+        <!-- 鎿嶄綔鎸夐挳 -->
+        <el-row :gutter="10" class="mb8">
+          <el-col :span="1.5">
+            <el-button v-hasPermi="['workflow:listener:add']" icon="Plus" plain type="primary" @click="handleAdd">鏂板</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button v-hasPermi="['workflow:listener:edit']" :disabled="single" icon="Edit" plain type="success" @click="handleUpdate()"
+              >淇敼</el-button
+            >
+          </el-col>
+          <el-col :span="1.5">
+            <el-button v-hasPermi="['workflow:listener:remove']" :disabled="multiple" icon="Delete" plain type="danger" @click="handleDelete()"
+              >鍒犻櫎</el-button
+            >
+          </el-col>
+          <el-col :span="1.5">
+            <el-button v-hasPermi="['workflow:listener:export']" icon="Download" plain type="warning" @click="handleExport">瀵煎嚭</el-button>
+          </el-col>
+          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+        </el-row>
+      </template>
+
+      <!-- 鍒嗛〉鍒楄〃 -->
+      <el-table
+        v-loading="tableLoading"
+        :data="listenerList"
+        @selection-change="handleSelectionChange"
+        v-fixTableHeight
+        highlight-current-row
+        show-overflow-tooltip
+      >
+        <el-table-column align="center" type="selection" width="55" />
+        <el-table-column type="expand" width="50px">
+          <template #default="props">
+            <el-table :data="props.row.fields" width="60%">
+              <el-table-column v-if="false" label="涓婚敭" prop="id" />
+              <el-table-column label="瀛楁鍚嶇О" prop="fieldName" />
+              <el-table-column label="瀛楁绫诲瀷" prop="fieldType">
+                <template #default="scope">
+                  <dict-tag :options="sys_listener_field_type" :value="scope.row.fieldType" />
+                </template>
+              </el-table-column>
+              <el-table-column label="瀛楁鍊�" prop="fieldValue" />
+              <el-table-column align="center" class-name="small-padding fixed-width" label="鎿嶄綔">
+                <template #default="scope">
+                  <el-tooltip content="淇敼" placement="top">
+                    <el-button
+                      v-hasPermi="['workflow:listener:edit']"
+                      icon="Edit"
+                      link
+                      type="primary"
+                      @click="handleFieldUpdate(scope.row)"
+                    ></el-button>
+                  </el-tooltip>
+                  <el-tooltip content="鍒犻櫎" placement="top">
+                    <el-button
+                      v-hasPermi="['workflow:listener:remove']"
+                      icon="Delete"
+                      link
+                      type="danger"
+                      @click="handleFieldDelete(scope.row)"
+                    ></el-button>
+                  </el-tooltip>
+                </template>
+              </el-table-column>
+            </el-table>
+          </template>
+        </el-table-column>
+        <el-table-column v-if="false" align="center" label="涓婚敭" prop="id" />
+        <el-table-column align="center" label="鐩戝惉绫诲瀷" prop="listenerType">
+          <template #default="scope">
+            <dict-tag :options="sys_listener_type" :value="scope.row.listenerType" />
+          </template>
+        </el-table-column>
+        <el-table-column align="center" label="鍚嶇О" prop="name" />
+        <el-table-column align="center" label="鍊肩被鍨�" prop="valueType">
+          <template #default="scope">
+            <dict-tag :options="sys_listener_value_type" :value="scope.row.valueType" />
+          </template>
+        </el-table-column>
+        <el-table-column align="center" label="鍊�" prop="value" />
+        <el-table-column align="center" label="浜嬩欢绫诲瀷" prop="eventType">
+          <template #default="scope">
+            <dict-tag v-if="scope.row.listenerType === 'task'" :options="sys_task_listener_event_type" :value="scope.row.eventType" />
+            <dict-tag v-if="scope.row.listenerType === 'execution'" :options="sys_execution_listener_event_type" :value="scope.row.eventType" />
+          </template>
+        </el-table-column>
+        <el-table-column align="center" class-name="small-padding fixed-width" label="鎿嶄綔">
+          <template #default="scope">
+            <el-tooltip content="娣诲姞瀛楁" placement="top">
+              <el-button v-hasPermi="['workflow:listener:edit']" icon="Plus" link type="primary" @click="addField(scope.row)"></el-button>
+            </el-tooltip>
+            <el-tooltip content="淇敼" placement="top">
+              <el-button v-hasPermi="['workflow:listener:edit']" icon="Edit" link type="primary" @click="handleUpdate(scope.row)"></el-button>
+            </el-tooltip>
+            <el-tooltip content="鍒犻櫎" placement="top">
+              <el-button v-hasPermi="['workflow:listener:remove']" icon="Delete" link type="danger" @click="handleDelete(scope.row)"></el-button>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+      </el-table>
+      <pagination v-show="total > 0" v-model:limit="queryParams.pageSize" v-model:page="queryParams.pageNum" :total="total" @pagination="getList" />
+    </el-card>
+
+    <!-- 娣诲姞鎴栦慨鏀规祦绋嬭〃鍗曞璇濇 -->
+    <el-dialog v-model="dialog.visible" :close-on-click-modal="false" :title="dialog.title" append-to-body width="600px">
+      <el-form ref="listenerFormRef" v-loading="formLoading" :model="form" :rules="rules" label-width="120px">
+        <el-form-item label="鐩戝惉绫诲瀷" prop="listenerType">
+          <el-radio-group v-model="form.listenerType">
+            <el-radio v-for="dict in sys_listener_type" :key="dict.value" :label="dict.value">{{ dict.label }}</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="鍚嶇О" prop="name">
+          <el-input v-model="form.name" placeholder="璇疯緭鍏ュ悕绉�" />
+        </el-form-item>
+        <el-form-item label="鍊肩被鍨�" prop="valueType">
+          <el-radio-group v-model="form.valueType">
+            <el-radio v-for="dict in sys_listener_value_type" :key="dict.value" :label="dict.value">{{ dict.label }}</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="鍊�" prop="value">
+          <el-input v-model="form.value" placeholder="璇疯緭鍏ュ��" />
+        </el-form-item>
+        <el-form-item label="浜嬩欢绫诲瀷" prop="eventType">
+          <el-select v-model="form.eventType" clearable placeholder="璇烽�夋嫨浜嬩欢绫诲瀷" style="width: 100%">
+            <el-option
+              v-for="dict in sys_task_listener_event_type"
+              v-if="form.listenerType === 'task'"
+              :key="dict.value"
+              :label="dict.label"
+              :value="dict.value"
+            />
+            <el-option
+              v-for="dict in sys_execution_listener_event_type"
+              v-if="form.listenerType === 'execution'"
+              :key="dict.value"
+              :label="dict.label"
+              :value="dict.value"
+            />
+          </el-select>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="submitForm">纭� 瀹�</el-button>
+          <el-button @click="cancel">鍙� 娑�</el-button>
+        </div>
+      </template>
+    </el-dialog>
+
+    <!-- 鐩戝惉鍣ㄥ瓧娈靛璇濇 -->
+    <el-dialog v-model="fieldDialog.visible" :close-on-click-modal="false" :title="fieldDialog.title" append-to-body width="600px">
+      <el-form ref="listenerFieldFormRef" v-loading="fieldFormLoading" :model="initFieldFormData" :rules="filedRules" label-width="120px">
+        <el-form-item label="鍚嶇О" prop="fieldName">
+          <el-input v-model="filedForm.fieldName" placeholder="璇疯緭鍏ュ悕绉�" />
+        </el-form-item>
+        <el-form-item label="瀛楁绫诲瀷" prop="fieldType">
+          <el-radio-group v-model="filedForm.fieldType">
+            <el-radio v-for="dict in sys_listener_field_type" :key="dict.value" :label="dict.value">{{ dict.label }}</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="鍊�" prop="fieldValue">
+          <el-input v-model="filedForm.fieldValue" placeholder="璇疯緭鍏ュ��" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="submitFieldForm">纭� 瀹�</el-button>
+          <el-button @click="cancel">鍙� 娑�</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script  name="Form" setup>
+getList();
+
+import {
+  addListener,
+  deleteListenerFieldAPI,
+  delListener,
+  getListener,
+  insertListenerFieldAPI,
+  queryListenerPage,
+  updateListener,
+  updateListenerFieldAPI
+} from "#/api/workflow/listener";
+
+
+
+const { proxy } = getCurrentInstance() ;
+const {
+  sys_listener_type,
+  sys_listener_value_type,
+  sys_task_listener_event_type,
+  sys_execution_listener_event_type,
+  sys_listener_field_type
+} = proxy.useDict("sys_listener_type", "sys_listener_value_type", "sys_task_listener_event_type", "sys_execution_listener_event_type", "sys_listener_field_type");
+
+const listenerList = ref([]);
+const showSearch = ref(true);
+const ids = ref([]);
+const names = ref([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+const tableLoading = ref(false);
+const formLoading = ref(false);
+const fieldFormLoading = ref(false);
+const listenerFormRef = ref();
+const listenerFieldFormRef = ref();
+const queryFormRef = ref();
+const listenerId = ref('');
+
+const dialog = reactive({
+  visible: false,
+  title: ''
+});
+
+const fieldDialog = reactive({
+  visible: false,
+  title: ''
+});
+const initFormData = {
+  id: undefined,
+  valueType: '',
+  eventType: '',
+  listenerType: '',
+  name: '',
+  value: ''
+}
+
+const data = reactive({
+  form: {...initFormData},
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    name: '',
+    listenerType: '',
+    eventType: ''
+  },
+  rules: {
+    listenerType: [{ required: true, message: "鐩戝惉绫诲瀷涓嶈兘涓虹┖", trigger: "blur" }],
+    name: [{ required: true, message: "鍚嶇О涓嶈兘涓虹┖", trigger: "blur" }],
+    valueType: [{ required: true, message: "鍊肩被鍨嬩笉鑳戒负绌�", trigger: "blur" }],
+    value: [{ required: true, message: "鍊间笉鑳戒负绌�", trigger: "blur" }]
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+const initFieldFormData={
+  id: undefined,
+  listenerId: undefined,
+  fieldName: undefined,
+  fieldType: undefined,
+  fieldValue: undefined
+}
+
+const filedData = reactive({
+  filedForm: {...initFieldFormData},
+  filedRules: {
+    fieldName: [{ required: true, message: "璇疯緭鍏ュ瓧娈靛悕绉�", trigger: "blur" }],
+    fieldType: [{ required: true, message: "璇烽�夋嫨瀛楁绫诲瀷", trigger: "blur" }]
+  }
+})
+
+const { filedForm, filedRules } = toRefs(filedData);
+
+/** 鏌ヨ鍒楄〃 */
+const getList = async () => {
+  tableLoading.value = true;
+  const res = await queryListenerPage(queryParams.value);
+  listenerList.value = res.rows;
+  total.value = res.total;
+  tableLoading.value = false;
+}
+/** 鍙栨秷鎸夐挳 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+  fieldDialog.visible = false;
+}
+/** 琛ㄥ崟閲嶇疆 */
+const reset = () => {
+  form.value = {...initFormData};
+  filedForm.value = {...initFieldFormData};
+}
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+}
+/** 閲嶇疆鎸夐挳鎿嶄綔 */
+const resetQuery = () => {
+  queryFormRef.value.resetFields();
+  handleQuery();
+}
+/** 澶氶�夋閫変腑鏁版嵁 */
+const handleSelectionChange = (selection) => {
+  ids.value = selection.map(item => item.id);
+  names.value = selection.map(item => item.name);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+}
+/** 鏂板琛ㄥ崟鎿嶄綔 */
+const handleAdd = () => {
+  dialog.visible = true;
+  nextTick(() => {
+    reset();
+  })
+}
+/** 淇敼琛ㄥ崟鎿嶄綔 */
+const handleUpdate = (row) => {
+  dialog.visible = true;
+  const id = row?.id || ids.value[0];
+  nextTick(async () => {
+    formLoading.value = true;
+    reset();
+    const res = await getListener(id);
+    formLoading.value = false;
+    form.value = res.data;
+  })
+}
+/** 鎻愪氦琛ㄥ崟鎿嶄綔 */
+const submitForm = () => {
+  listenerFormRef.value.validate(async (valid) => {
+    if (valid) {
+      dialog.visible = true;
+      formLoading.value = true;
+      try {
+        form.value.id ? await updateListener(form.value) : await addListener(form.value);
+      } finally {
+        formLoading.value = false;
+      }
+      dialog.visible = false;
+      proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+      getList();
+    }
+  });
+}
+/** 鍒犻櫎鎸夐挳鎿嶄綔 */
+const handleDelete = async (row) => {
+  const listenerIds = row?.id || ids.value;
+  const listenerNames = row?.name || ids.value;
+  await proxy?.$modal.confirm('鏄惁纭鍒犻櫎銆�' + listenerNames + '銆戠殑鏁版嵁椤癸紵');
+  tableLoading.value = true;
+  try {
+    await delListener(listenerIds);
+  } finally {
+    tableLoading.value = false;
+  }
+  getList();
+  proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+}
+/** 娣诲姞瀛楁鎿嶄綔 */
+const addField = (ro) => {
+  fieldDialog.visible = true;
+  // listenerField.value = row.fields;
+  listenerId.value = row.id;
+  nextTick(() => {
+    reset();
+    filedForm.value.listenerId = row.id;
+  })
+}
+/** 淇敼瀛楁鎿嶄綔 */
+const handleFieldUpdate = (row) => {
+  fieldDialog.visible = true;
+  filedForm.value.id = row.id;
+  filedForm.value.listenerId = row.listenerId;
+  filedForm.value.fieldName = row.fieldName;
+  filedForm.value.fieldType = row.fieldType;
+  filedForm.value.fieldValue = row.fieldValue;
+}
+/** 鎻愪氦瀛楁琛ㄥ崟鎿嶄綔 */
+const submitFieldForm = () => {
+  listenerFieldFormRef.value.validate(async (vali) => {
+    if (valid) {
+      fieldDialog.visible = true;
+      fieldFormLoading.value = true;
+      try {
+        filedForm.value.id ? await updateListenerFieldAPI(filedForm.value) : await insertListenerFieldAPI(filedForm.value);
+      } finally {
+        fieldFormLoading.value = false;
+      }
+      listenerId.value = '';
+      fieldDialog.visible = false;
+      proxy.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+      getList();
+    }
+  });
+}
+/** 鍒犻櫎瀛楁鎸夐挳鎿嶄綔 */
+const handleFieldDelete = async (ro) => {
+  const fieldIds = row?.id || ids.value;
+  const fieldNames = row?.fieldName || ids.value;
+  await proxy?.$modal.confirm('鏄惁纭鍒犻櫎銆�' + fieldNames + '銆戠殑鏁版嵁椤癸紵');
+  tableLoading.value = true;
+  try {
+    await deleteListenerFieldAPI(fieldIds);
+  } finally {
+    tableLoading.value = false;
+  }
+  getList();
+  proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+}
+/** 瀵煎嚭鎸夐挳鎿嶄綔 */
+const handleExport = () => {
+  proxy?.download("workflow/form/export", {
+    ...queryParams.value
+  }, `form_${new Date().getTime()}.xlsx`);
+}
+
+</script>
diff --git a/ruoyi-ui/apps/web-antd/src/views/workflow/model/index.vue b/ruoyi-ui/apps/web-antd/src/views/workflow/model/index.vue
new file mode 100644
index 0000000..baa47df
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/views/workflow/model/index.vue
@@ -0,0 +1,438 @@
+<template>
+ <div class="app-container">
+      <div class="search" v-show="showSearch">
+        <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="70">
+          <el-form-item label="妯″瀷鏍囪瘑" prop="modelKey">
+            <el-input v-model="queryParams.modelKey" placeholder="璇疯緭鍏ユā鍨嬫爣璇�" clearable style="width: 200px" @keyup.enter="handleQuery" />
+          </el-form-item>
+          <el-form-item label="妯″瀷鍚嶇О" prop="modelName">
+            <el-input v-model="queryParams.modelName" placeholder="璇疯緭鍏ユā鍨嬪悕绉�" clearable style="width: 200px" @keyup.enter="handleQuery" />
+          </el-form-item>
+          <el-form-item label="娴佺▼鍒嗙被" prop="category">
+            <el-select v-model="queryParams.category" clearable placeholder="璇烽�夋嫨" @change="handleQuery"  style="width: 240px">
+              <el-option
+                v-for="item in categoryOptions"
+                :key="item.categoryId"
+                :label="item.categoryName"
+                :value="item.code">
+              </el-option>
+            </el-select>
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
+            <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+          </el-form-item>
+        </el-form>
+      </div>
+    <el-card shadow="never">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+          <el-col :span="1.5">
+            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['workflow:model:add']">鏂板</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete" v-hasPermi="['workflow:model:remove']">鍒犻櫎</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['workflow:model:export']">瀵煎嚭</el-button>
+          </el-col>
+          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" :data="modelList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="妯″瀷鏍囪瘑" align="center" prop="modelKey" :show-overflow-tooltip="true" />
+        <el-table-column label="妯″瀷鍚嶇О" align="center" :show-overflow-tooltip="true">
+          <template #default="scope">
+            <a class="link-type" @click="handleProcessView(scope.row)">
+              <span>{{ scope.row.modelName }}</span>
+            </a>
+          </template>
+        </el-table-column>
+        <el-table-column label="娴佺▼鍒嗙被" align="center" prop="categoryName" :formatter="categoryFormat" />
+        <el-table-column label="妯″瀷鐗堟湰" align="center">
+          <template #default="scope">
+            <el-tag size="small">v{{ scope.row.version }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="鎻忚堪" align="center" prop="description" :show-overflow-tooltip="true" />
+        <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="180" >
+          <template #default="scope">
+              {{  parseTime(scope.row.createTime, '{y}-{m}-{d}  {h}:{i}:{s}')}}
+          </template>
+        </el-table-column>
+        <el-table-column label="鎿嶄綔" width="180" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-tooltip content="淇敼" placement="top">
+              <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['workflow:model:edit']"></el-button>
+            </el-tooltip>
+            <el-tooltip content="鍒犻櫎" placement="top">
+              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['workflow:model:remove']"></el-button>
+            </el-tooltip>
+            <el-tooltip content="璁捐" placement="top">
+              <el-button link type="primary" icon="Brush" @click="handleDesigner(scope.row)" v-hasPermi="['workflow:model:designer']"></el-button>
+            </el-tooltip>
+            <el-tooltip content="閮ㄧ讲" placement="top">
+              <el-button link type="primary" icon="Promotion" @click="handleDeploy(scope.row)" v-hasPermi="['workflow:model:deploy']"></el-button>
+            </el-tooltip>
+            <el-tooltip content="鍘嗗彶" placement="top">
+              <el-button link type="primary" icon="Discount" @click="handleHistory(scope.row)" v-hasPermi="['workflow:model:list']"></el-button>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+    </el-card>
+
+    <!--  娣诲姞鎴栦慨鏀规ā鍨嬩俊鎭璇濇  -->
+    <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+      <el-form ref="modelFormRef" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="妯″瀷鏍囪瘑" prop="modelKey">
+          <el-input v-model="form.modelKey" clearable disabled placeholder="璇疯緭鍏ユā鍨嬫爣璇�" />
+        </el-form-item>
+        <el-form-item label="妯″瀷鍚嶇О" prop="modelName">
+          <el-input v-model="form.modelName" clearable :disabled="form.modelId !== undefined" placeholder="璇疯緭鍏ユā鍨嬪悕绉�" />
+        </el-form-item>
+        <el-form-item label="娴佺▼鍒嗙被" prop="category">
+          <el-select v-model="form.category" placeholder="璇烽�夋嫨" clearable style="width:100%">
+            <el-option v-for="item in categoryOptions" :key="item.categoryId" :label="item.categoryName" :value="item.code" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="鎻忚堪" prop="description">
+          <el-input v-model="form.description" type="textarea" placeholder="璇疯緭鍏ュ唴瀹�" maxlength="200" show-word-limit />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button type="primary" @click="submitForm">纭� 瀹�</el-button>
+          <el-button @click="cancel">鍙� 娑�</el-button>
+        </div>
+      </template>
+    </el-dialog>
+
+    <el-dialog :title="designer.title" v-model="designer.visible" append-to-body fullscreen>
+      <ProcessDesigner
+        :key="`designer-${reloadIndex}`"
+        ref="modelDesignerRef"
+        v-loading="designerLoading"
+        :designer-form="designerForm"
+        :bpmn-xml="bpmnXml"
+        @save="onSaveDesigner"
+      />
+    </el-dialog>
+
+    <el-dialog :title="history.title" v-model="history.visible" append-to-body>
+      <el-table v-loading="historyLoading" :data="historyList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="妯″瀷鏍囪瘑" align="center" prop="modelKey" :show-overflow-tooltip="true" />
+        <el-table-column label="妯″瀷鍚嶇О" align="center" prop="modelName" :show-overflow-tooltip="true" />
+        <el-table-column label="娴佺▼鍒嗙被" align="center" prop="categoryName" :formatter="categoryFormat" />
+        <el-table-column label="妯″瀷鐗堟湰" align="center">
+          <template #default="scope">
+            <el-tag>v{{ scope.row.version }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="鎻忚堪" align="center" prop="description" :show-overflow-tooltip="true" />
+        <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="180" />
+        <el-table-column label="鎿嶄綔" width="180" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-tooltip content="閮ㄧ讲" placement="top">
+              <el-button link type="primary" icon="Promotion" @click="handleDeploy(scope.row)" v-hasPermi="['workflow:model:deploy']"></el-button>
+            </el-tooltip>
+            <el-tooltip content="璁句负鏈�鏂�" placement="top">
+              <el-button link type="primary" icon="Star" @click="handleLatest(scope.row)" v-hasPermi="['workflow:model:save']"></el-button>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+    </el-dialog>
+
+    <!-- 娴佺▼鍥� -->
+    <el-dialog :title="processDialog.title" v-model="processDialog.visible" width="70%">
+      <ProcessViewer :key="`designer-${reloadIndex}`" :xml="processXml" :style="{height: '650px'}" />
+    </el-dialog>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { getBpmnXml, listModel, historyModel, latestModel, addModel, updateModel, saveModel, delModel, deployModel, getModel } from "#/api/workflow/model";
+import { listAllCategory } from "#/api/workflow/category";
+
+import {getCurrentInstance,ref,reactive,toRefs,onMounted} from "vue";
+import {useRouter} from "vue-router";
+import { defineAsyncComponent } from "vue";
+// 寮傛鍔犺浇缁勪欢
+const ProcessDesigner = defineAsyncComponent(() =>
+  import('#/components/ProcessDesigner/index.vue')
+);
+const ProcessViewer = defineAsyncComponent(() =>
+  import('#/components/ProcessViewer/index.vue')
+);
+const { proxy } = getCurrentInstance() ;
+
+const modelList = ref([]);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+const categoryOptions = ref([]);
+const designerLoading = ref(true);
+const bpmnXml = ref('');
+const reloadIndex = ref(0);
+const processXml = ref("");
+
+const historyList = ref([]);
+const historyLoading = ref(true);
+const historyTotal = ref(0);
+
+const modelFormRef = ref();
+const queryFormRef = ref();
+const modelDesignerRef = ref(null)
+
+const dialog = reactive({
+  visible: false,
+  title: ''
+});
+
+const processDialog = reactive({
+  visible: false,
+  title: '娴佺▼鍥�'
+});
+
+const designer = reactive({
+  visible: false,
+  title: ''
+});
+
+const history = reactive({
+  visible: false,
+  title: ''
+});
+
+const initFormData={
+  modelId: undefined,
+  modelKey: `Process_${new Date().getTime()}`,
+  modelName: `涓氬姟娴佺▼_${new Date().getTime()}`,
+  category: '',
+  description: '',
+  formType: undefined,
+  formId: undefined,
+  bpmnXml: '',
+  newVersion: false
+}
+
+const data = reactive({
+  form: {...initFormData},
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    modelKey: '',
+    modelName: '',
+    category: ''
+  },
+  rules: {
+    modelKey: [{ required: true, message: "宀椾綅鍚嶇О涓嶈兘涓虹┖", trigger: "blur" }],
+    modelName: [{ required: true, message: "宀椾綅缂栫爜涓嶈兘涓虹┖", trigger: "blur" }],
+  }
+});
+
+const designerForm = reactive({
+  modelId: '',
+  form: {
+    processName: '',
+    processKey: ''
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+const router = useRouter();
+
+/** 鏌ヨ妯″瀷鍒楄〃 */
+const getList = async () => {
+  // loading.value = true;
+  const res = await listModel(queryParams.value);
+  console.log(res)
+  modelList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+}
+/** 鍙栨秷鎸夐挳 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+}
+/** 琛ㄥ崟閲嶇疆 */
+const reset = () => {
+  form.value = {...initFormData};
+  modelFormRef.value.resetFields();
+}
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+}
+/** 閲嶇疆鎸夐挳鎿嶄綔 */
+const resetQuery = () => {
+  queryFormRef.value.resetFields();
+  handleQuery();
+}
+/** 澶氶�夋閫変腑鏁版嵁 */
+const handleSelectionChange = (selection) => {
+  ids.value = selection.map(item => item.modelId);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+}
+/** 鏂板鎸夐挳鎿嶄綔 */
+const handleAdd = () => {
+  dialog.visible = true;
+  dialog.title = "娣诲姞妯″瀷";
+  nextTick(() => {
+    reset();
+  })
+}
+/** 淇敼鎸夐挳鎿嶄綔 */
+const handleUpdate = (row) => {
+  dialog.visible = true;
+  dialog.title = "淇敼妯″瀷";
+  nextTick(async () => {
+    reset();
+    const modelId = row.modelId || ids.value[0];
+    const res = await getModel(modelId);
+    form.value = res.data;
+  });
+};
+/** 鍒犻櫎鎸夐挳鎿嶄綔 */
+const handleDelete = async (row) => {
+  const modelIds = row.modelId || ids.value;
+  await proxy?.$modal.confirm('鏄惁纭鍒犻櫎鍙傛暟缂栧彿涓�"' + modelIds + '"鐨勬暟鎹」锛�');
+  await delModel(modelIds);
+  getList();
+  proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+}
+/** 瀵煎嚭鎸夐挳鎿嶄綔 */
+const handleExport = () => {
+  proxy?.download("workflow/model/export", {
+    ...queryParams.value
+  }, `model_${new Date().getTime()}.xlsx`);
+};
+/** 鏌ョ湅娴佺▼鍥� */
+const handleProcessView = async (row) => {
+  reloadIndex.value++;
+  // 鍙戦�佽姹傦紝鑾峰彇xml
+  const res = await getBpmnXml(row.modelId);
+  processXml.value = res.data;
+  processDialog.visible = true;
+}
+/** 璁捐鎸夐挳鎿嶄綔 */
+const handleDesigner = async (row) => {
+  reloadIndex.value++;
+  designerForm.modelId = row.modelId;
+  const res = await getBpmnXml(row.modelId);
+  bpmnXml.value = res.data || '';
+  designerLoading.value = false;
+  designer.title = "娴佺▼璁捐 - " + row.modelName;
+  designer.visible = true;
+}
+const handleDeploy = (row) => {
+  loading.value = true;
+  nextTick(async () => {
+    await deployModel({ modelId: row?.modelId });
+    proxy.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+    router.push({
+      name: 'Deploy',
+      path: '/workflow/deploy'
+    });
+    loading.value = false;
+  });
+}
+const handleLatest = async (row) => {
+  await proxy.$modal.confirm('鏄惁灏嗘妯″瀷淇濆瓨涓烘柊鐗堟湰?');
+  historyLoading.value = true;
+  await latestModel({modelId: row.modelId});
+  history.visible = false;
+  getList();
+  proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+  historyLoading.value = false;
+}
+/** 鏌ヨ鍘嗗彶鍒楄〃 */
+const getHistoryList = async () => {
+  historyLoading.value = true;
+  const res = await historyModel(queryParams.value);
+  historyList.value = res.rows;
+  historyTotal.value = res.total;
+  historyLoading.value = false;
+}
+const handleHistory = (row) => {
+  history.visible = true;
+  history.title = "妯″瀷鍘嗗彶";
+  queryParams.value.modelKey = row?.modelKey;
+  getHistoryList();
+}
+/** 鎻愪氦琛ㄥ崟鎿嶄綔 */
+const submitForm = () => {
+  modelFormRef.value.validate(async (valid) => {
+    if (valid) {
+      form.value.modelId ? await updateModel(form.value) : await addModel(form.value);
+      proxy.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+      dialog.visible = false;
+      getList();
+    }
+  })
+}
+/** 鏌ヨ娴佺▼鍒嗙被鍒楄〃 */
+const getCategoryList = async () => {
+  const res = await listAllCategory();
+  categoryOptions.value = res.data;
+}
+
+const onSaveDesigner = async (str) => {
+  bpmnXml.value = str;
+  let dataBody = {
+    modelId: designerForm.modelId,
+    bpmnXml: str
+  }
+  proxy.$modal.confirm('鏄惁灏嗘妯″瀷淇濆瓨涓烘柊鐗堟湰?').then(() => {
+    confirmSave(dataBody, true)
+  }).catch(action => {
+    if (action === 'cancel') {
+      confirmSave(dataBody, false)
+    }
+  })
+}
+const confirmSave = async (body, newVersion) => {
+  designerLoading.value = true;
+  console.log(body,"body");
+  await saveModel(Object.assign(body, { newVersion: newVersion }));
+  getList();
+  proxy.$modal.msgSuccess("淇濆瓨鎴愬姛");
+  designerLoading.value = false;
+  designer.visible = false;
+}
+
+const categoryFormat = (row) => {
+  var category = categoryOptions.value.find(function(k) {
+        return k.code === row.category;
+    });
+    return category ? category.categoryName : '';
+}
+
+onMounted(() => {
+  getCategoryList()
+  getList();
+});
+</script>
+
+<style lang="scss" scoped>
+.el-dialog__body {
+  max-height: calc(100vh) !important;
+  overflow-y: auto;
+  overflow-x: hidden;
+}
+</style>
diff --git a/ruoyi-ui/apps/web-antd/src/views/workflow/work/claim.vue b/ruoyi-ui/apps/web-antd/src/views/workflow/work/claim.vue
new file mode 100644
index 0000000..a272b65
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/views/workflow/work/claim.vue
@@ -0,0 +1,129 @@
+<template>
+ <div class="app-container">
+      <div class="search" v-show="showSearch">
+        <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="70">
+          <el-form-item label="娴佺▼鍚嶇О" prop="processName">
+            <el-input v-model="queryParams.processName" placeholder="璇疯緭鍏ユ祦绋嬪悕绉�" clearable style="width: 200px" @keyup.enter="handleQuery" />
+          </el-form-item>
+          <el-form-item label="娴佺▼鍒嗙被" prop="category">
+            <el-select v-model="queryParams.category" clearable placeholder="璇烽�夋嫨"  style="width: 240px">
+              <el-option v-for="item in categoryOptions" :key="item.categoryId" :label="item.categoryName" :value="item.code" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="鎺ユ敹鏃堕棿" style="width: 308px;">
+            <el-date-picker
+              v-model="dateRange"
+              value-format="YYYY-MM-DD"
+              type="daterange"
+              range-separator="-"
+              start-placeholder="寮�濮嬫棩鏈�"
+              end-placeholder="缁撴潫鏃ユ湡"
+            ></el-date-picker>
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
+            <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+          </el-form-item>
+        </el-form>
+      </div>
+    <el-card shadow="never">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+        </el-row>
+      </template>
+
+
+      <el-table v-loading="loading" :data="claimList">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="浠诲姟缂栧彿" align="center" prop="taskId" :show-overflow-tooltip="true" />
+        <el-table-column label="娴佺▼鍚嶇О" align="center" prop="procDefName" />
+        <el-table-column label="浠诲姟鑺傜偣" align="center" prop="taskName" />
+        <el-table-column label="娴佺▼鐗堟湰" align="center">
+          <template #default="scope">
+            <el-tag>v{{scope.row.procDefVersion}}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="娴佺▼鍙戣捣浜�" align="center" prop="startUserName" />
+        <el-table-column label="鎺ユ敹鏃堕棿" align="center" prop="createTime" width="180" />
+        <el-table-column label="鎿嶄綔" width="180" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-tooltip content="绛炬敹" placement="top">
+              <el-button link type="primary" icon="EditPen" @click="handleClaim(scope.row)" v-hasPermi="['workflow:process:claim']"></el-button>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+    </el-card>
+  </div>
+</template>
+
+<script setup name="Claim" lang="js">
+import { listClaimProcess } from "#/api/workflow/work/process";
+import { claimTask } from "#/api/workflow/work/task";
+
+import { listAllCategory } from "#/api/workflow/category";
+
+const router = useRouter();
+const { proxy } = getCurrentInstance() ;
+
+const claimList = ref([]);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+const dateRange = ref(['','']);
+const categoryOptions = ref([]);
+const queryFormRef = ref();
+
+const queryParams = ref({
+  pageNum: 1,
+  pageSize: 10,
+  processName: ''
+});
+
+/** 鏌ヨ娴佺▼鍒嗙被鍒楄〃 */
+const getCategoryList = async () => {
+  const res = await listAllCategory();
+  categoryOptions.value = res.data;
+}
+/** 鏌ヨ寰呭姙鍒楄〃 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listClaimProcess(proxy.addDateRange(queryParams.value, dateRange.value));
+  claimList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+}
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+}
+/** 閲嶇疆鎸夐挳鎿嶄綔 */
+const resetQuery = () => {
+  queryFormRef.value.resetFields();
+  handleQuery();
+}
+/** 绛炬敹鎸夐挳鎿嶄綔 */
+const handleClaim = async (row) => {
+  const res = await claimTask({ taskId: row.taskId })
+  proxy.$modal.msgSuccess(res.msg);
+  router.push({ path: '/work/todo' })
+}
+
+const categoryFormat = (row) => {
+  var category = categoryOptions.value.find(function(k) {
+        return k.code === row.category;
+    });
+    return category && category.categoryName ? category.categoryName : '';
+}
+onMounted(() => {
+  getCategoryList();
+  getList();
+});
+</script>
diff --git a/ruoyi-ui/apps/web-antd/src/views/workflow/work/copy.vue b/ruoyi-ui/apps/web-antd/src/views/workflow/work/copy.vue
new file mode 100644
index 0000000..6fdfe88
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/views/workflow/work/copy.vue
@@ -0,0 +1,122 @@
+<template>
+ <div class="app-container">
+      <div class="search" v-show="showSearch">
+        <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="70">
+          <el-form-item label="娴佺▼鍚嶇О" prop="processName">
+            <el-input v-model="queryParams.processName" placeholder="璇疯緭鍏ユ祦绋嬪悕绉�" clearable style="width: 200px" @keyup.enter="handleQuery" />
+          </el-form-item>
+          <el-form-item label="娴佺▼鍒嗙被" prop="category">
+            <el-select v-model="queryParams.category" clearable placeholder="璇烽�夋嫨"  style="width: 240px">
+              <el-option v-for="item in categoryOptions" :key="item.categoryId" :label="item.categoryName" :value="item.code" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="鍙戣捣浜�" prop="originatorName">
+            <el-input v-model="queryParams.originatorName" placeholder="璇疯緭鍏ュ彂璧蜂汉" clearable style="width: 200px" @keyup.enter="handleQuery" />
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
+            <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+          </el-form-item>
+        </el-form>
+      </div>
+    <el-card shadow="never">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" :data="copyList">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="鎶勯�佺紪鍙�" align="center" prop="copyId" />
+        <el-table-column label="鏍囬" align="center" prop="title" :show-overflow-tooltip="true" />
+        <el-table-column label="娴佺▼鍚嶇О" align="center" prop="processName" :show-overflow-tooltip="true" />
+        <el-table-column label="娴佺▼鍒嗙被" align="center" prop="categoryId" :formatter="categoryFormat" />
+        <el-table-column label="鍙戣捣浜�" align="center" prop="originatorName" />
+        <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime">
+          <template #default="scope">
+            <span>{{ parseTime(scope.row.createTime) }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="鎿嶄綔" width="180" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-tooltip content="璇︽儏" placement="top">
+              <el-button link type="primary" icon="View" @click="handleDetails(scope.row)" v-hasPermi="['workflow:process:query']"></el-button>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+    </el-card>
+  </div>
+</template>
+
+<script setup name="Copy" lang="js">
+import { listCopyProcess } from "#/api/workflow/work/process"
+const router = useRouter();
+import { listAllCategory } from "#/api/workflow/category";
+const { proxy } = getCurrentInstance() ;
+
+const copyList = ref([]);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+const categoryOptions = ref([]);
+const queryFormRef = ref();
+
+const queryParams = ref({
+  pageNum: 1,
+  pageSize: 10,
+  processName: "",
+  originatorName: "",
+  category: ''
+});
+
+/** 鏌ヨ娴佺▼鍒嗙被鍒楄〃 */
+const getCategoryList = async () => {
+  const res = await listAllCategory();
+  categoryOptions.value = res.data;
+}
+/** 鏌ヨ寰呭姙鍒楄〃 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listCopyProcess(queryParams.value);
+  copyList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+}
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+}
+/** 閲嶇疆鎸夐挳鎿嶄綔 */
+const resetQuery = () => {
+  queryFormRef.value.resetFields();
+  handleQuery();
+}
+/** 娴佺▼璇︽儏 */
+const handleDetails = (row) => {
+   router.push({
+     path: '/workflow/process/detail/' + row.instanceId,
+     query: {
+       processed: false
+     }
+   })
+}
+const categoryFormat = (row) => {
+  var category = categoryOptions.value.find(function(find) {
+        return find.code === row.categoryId;
+    });
+    return category && category.categoryName ? category.categoryName : '';
+}
+
+onMounted(() => {
+  getCategoryList();
+  getList();
+});
+</script>
diff --git a/ruoyi-ui/apps/web-antd/src/views/workflow/work/detail.vue b/ruoyi-ui/apps/web-antd/src/views/workflow/work/detail.vue
new file mode 100644
index 0000000..c61e310
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/views/workflow/work/detail.vue
@@ -0,0 +1,545 @@
+<template>
+  <div class="app-container">
+    <el-tabs tab-position="top" :model-value="processed === true ? 'approval' : 'form'">
+      <el-tab-pane label="浠诲姟鍔炵悊" name="approval" v-if="processed === true">
+        <el-card class="box-card" shadow="hover" v-if="taskFormOpen">
+          <template #header>
+            <span>濉啓琛ㄥ崟</span>
+          </template>
+          <div class="cu-content">
+            <v-form-render :form-json="{}" :form-data="{}" ref="vfRenderRef" />
+          </div>
+        </el-card>
+        <el-card class="box-card" shadow="hover">
+          <template #header>
+            <span>瀹℃壒娴佺▼</span>
+          </template>
+          <el-row>
+            <el-col :span="20" :offset="2">
+              <el-form ref="taskFormRef" :model="taskForm" :rules="rules" label-width="120px">
+                <el-form-item label="瀹℃壒鎰忚" prop="comment">
+                  <el-input type="textarea" :rows="5" v-model="taskForm.comment" placeholder="璇疯緭鍏� 瀹℃壒鎰忚" />
+                </el-form-item>
+                <el-form-item label="鎶勯�佷汉" prop="copyUserIds">
+                  <el-tag :key="index" v-for="(item, index) in copyUser" closable :disable-transitions="false" @close="handleClose('copy', item)">
+                    {{ item.nickName }}
+                  </el-tag>
+                  <el-button class="button-new-tag" type="primary" icon="el-icon-plus" circle @click="onSelectCopyUsers" />
+                </el-form-item>
+                <el-form-item label="鎸囧畾瀹℃壒浜�" prop="copyUserIds">
+                  <el-tag :key="index" v-for="(item, index) in nextUser" closable :disable-transitions="false" @close="handleClose('next', item)">
+                    {{ item.nickName }}
+                  </el-tag>
+                  <el-button class="button-new-tag" type="primary" icon="el-icon-plus" circle @click="onSelectNextUsers" />
+                </el-form-item>
+              </el-form>
+            </el-col>
+          </el-row>
+          <el-row :gutter="10" type="flex" justify="center">
+            <el-col :span="1.5">
+              <el-button icon="CircleCheck" type="success" @click="handleComplete">閫氳繃</el-button>
+            </el-col>
+            <el-col :span="1.5">
+              <el-button icon="ChatLineSquare" type="primary" @click="handleDelegate">濮旀淳</el-button>
+            </el-col>
+            <el-col :span="1.5">
+              <el-button icon="Switch" type="success" @click="handleTransfer">杞姙</el-button>
+            </el-col>
+            <el-col :span="1.5">
+              <el-button icon="RefreshLeft" type="warning" @click="handleReturn">閫�鍥�</el-button>
+            </el-col>
+            <el-col :span="1.5">
+              <el-button icon="CircleClose" type="danger" @click="handleReject">鎷掔粷</el-button>
+            </el-col>
+          </el-row>
+        </el-card>
+      </el-tab-pane>
+
+      <el-tab-pane label="琛ㄥ崟淇℃伅" name="form">
+        <div v-if="formVisible">
+          <el-card class="box-card" shadow="never" v-for="(item, index) in processFormList" :key="index">
+            <template #header>
+              <span>{{ item.title }}</span>
+            </template>
+            <!--娴佺▼澶勭悊琛ㄥ崟妯″潡-->
+            <div class="cu-content">
+              <v-form-render :form-json="item.formModel" :form-data="item.formData" ref="vFormRenderRef" />
+            </div>
+          </el-card>
+        </div>
+      </el-tab-pane>
+
+      <el-tab-pane label="娴佽浆璁板綍" name="record">
+        <el-card class="box-card" shadow="never">
+          <el-col :span="20" :offset="2">
+            <div class="block">
+              <el-timeline>
+                <el-timeline-item v-for="(item, index) in historyProcNodeList" :key="index" :type="tagType(item.endTime)">
+                  <p style="font-weight: 700">{{ item.activityName }}</p>
+                  <el-card v-if="item.activityType === 'startEvent'" class="box-card" shadow="hover">
+                    {{ item.assigneeName }} 鍦� {{ item.createTime }} 鍙戣捣娴佺▼
+                  </el-card>
+                  <el-card v-if="item.activityType === 'userTask'" class="box-card" shadow="hover">
+                    <el-descriptions :column="5" :labelStyle="{'font-weight': 'bold'}">
+                      <el-descriptions-item label="瀹為檯鍔炵悊">{{ item.assigneeName || '-'}}</el-descriptions-item>
+                      <el-descriptions-item label="鍊欓�夊姙鐞�">{{ item.candidate || '-'}}</el-descriptions-item>
+                      <el-descriptions-item label="鎺ユ敹鏃堕棿">{{ item.createTime || '-'}}</el-descriptions-item>
+                      <el-descriptions-item label="鍔炵粨鏃堕棿">{{ item.endTime || '-' }}</el-descriptions-item>
+                      <el-descriptions-item label="鑰楁椂">{{ item.duration || '-'}}</el-descriptions-item>
+                    </el-descriptions>
+                    <div v-if="item.commentList && item.commentList.length > 0">
+                      <div v-for="(comment, index) in item.commentList" :key="index">
+                        <el-divider content-position="left">
+                          <el-tag :type="approveTypeTag(comment.type)">{{ commentType(comment.type) }}</el-tag>
+                          <el-tag type="info" effect="plain">{{  parseTime(comment.time, '{y}-{m}-{d}  {h}:{i}:{s}')}}</el-tag>
+                        </el-divider>
+                        <span>{{ comment.fullMessage }}</span>
+                      </div>
+                    </div>
+                  </el-card>
+                  <el-card v-if="item.activityType === 'endEvent'" class="box-card" shadow="hover">
+                    {{ item.createTime }} 缁撴潫娴佺▼
+                  </el-card>
+                </el-timeline-item>
+              </el-timeline>
+            </div>
+          </el-col>
+        </el-card>
+      </el-tab-pane>
+
+      <el-tab-pane label="娴佺▼璺熻釜" name="track">
+        <el-card class="box-card" shadow="never">
+          <ProcessViewer
+            :key="`designer-${loadIndex}`"
+            :style="'height:' + height"
+            :xml="processXml"
+            :finishedInfo="finishedInfo"
+            :allCommentList="historyProcNodeList"
+          />
+        </el-card>
+      </el-tab-pane>
+    </el-tabs>
+
+    <!--閫�鍥炴祦绋�-->
+    <el-dialog :title="returnDialog.title" v-model="returnDialog.visible" width="40%" append-to-body>
+      <el-radio-group v-model="returnTaskKey">
+        <el-radio-button v-for="item in returnTaskList" :key="item.id" :label="item.id">
+          {{ item.name }}
+        </el-radio-button>
+      </el-radio-group>
+      <template #footer>
+        <el-button @click="returnDialog.visible = false">鍙� 娑�</el-button>
+        <el-button type="primary" @click="submitReturn">纭� 瀹�</el-button>
+      </template>
+    </el-dialog>
+
+    <el-dialog :title="userSelectDialog.title" v-model="userSelectDialog.visible" width="60%" append-to-body>
+      <el-row type="flex" :gutter="20">
+        <!--閮ㄩ棬鏁版嵁-->
+        <el-col :span="5">
+          <el-card shadow="never" style="height: 100%">
+            <template #header>
+              <span>閮ㄩ棬鍒楄〃</span>
+            </template>
+            <div class="head-container">
+              <el-input v-model="deptName" placeholder="璇疯緭鍏ラ儴闂ㄥ悕绉�" prefix-icon="Search" clearable />
+              <el-tree
+                :data="deptOptions"
+                :props="{ label: 'label', children: 'children' }"
+                :expand-on-click-node="false"
+               :filter-node-method="filterNode"
+                ref="deptTreeRef"
+                default-expand-all
+                @node-click="handleNodeClick"
+              />
+            </div>
+          </el-card>
+        </el-col>
+        <el-col :span="18">
+          <el-table
+            ref="userTable"
+            :key="userSelectType"
+            height="500"
+            v-loading="userLoading"
+            :data="userList"
+            highlight-current-row
+            @current-change="changeCurrentUser"
+            @selection-change="handleSelectionChange"
+          >
+            <el-table-column v-if="userSelectType === 'copy' || userSelectType === 'next'" width="55" type="selection" />
+            <el-table-column v-else width="30">
+              <template #default="scope">
+                <el-radio :label="scope.row.userId" v-model="currentUserId">{{''}}</el-radio>
+              </template>
+            </el-table-column>
+            <el-table-column label="鐢ㄦ埛鍚嶇О" align="center" prop="userName" />
+            <el-table-column label="鐢ㄦ埛鏄电О" align="center" prop="nickName" />
+            <el-table-column label="鎵嬫満" align="center" prop="phonenumber" />
+          </el-table>
+          <pagination :total="userTotal" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList(selectDeptId)" />
+        </el-col>
+      </el-row>
+      <template #footer>
+        <el-button @click="userSelectDialog.visible = false">鍙� 娑�</el-button>
+        <el-button type="primary" @click="submitUserData">纭� 瀹�</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="Detail" lang="js">
+import { detailProcess } from "#/api/workflow/work/process";
+import { complete, delegate, transfer, rejectTask, returnList, returnTask } from "#/api/workflow/work/task";
+import { deptTreeSelect, selectUser } from "#/api/workflow/identity";
+import ProcessViewer from "#/components/ProcessViewer";
+import {useRoute} from "vue-router";
+
+const route = useRoute();
+const router = useRouter();
+const { proxy } = getCurrentInstance() ;
+
+const userList = ref([]);
+const processed = ref(false);
+const taskFormOpen = ref(false)
+const userMultipleSelection = ref([]);
+const userSelectType = ref();
+const currentUserId = ref();
+const userLoading = ref(false);
+const userTotal = ref(0);
+const loadIndex = ref(0);
+const height = ref(document.documentElement.clientHeight - 205 + 'px;');
+const processXml = ref('');
+const taskFormVisible = ref(false);
+const processFormList = ref([]);
+const taskFormData = ref([]);
+const historyProcNodeList = ref();
+const formVisible = ref(false);
+const finishedInfo = ref({});
+
+const deptName = ref('');
+const selectDeptId = ref('');
+const deptOptions = ref([]);
+
+const returnTaskList = ref();
+const returnTaskKey = ref();
+
+const copyUser = ref([]);
+const nextUser = ref([]);
+
+const taskFormRef = ref();
+const vFormRenderRef = ref(null);
+const deptTreeRef = ref(null);
+
+const returnDialog = reactive({
+  visible: false,
+  title: '閫�鍥炴祦绋�'
+});
+
+const userSelectDialog = reactive({
+  visible: false,
+  title: ''
+});
+
+const taskForm = reactive({
+  comment: '',
+  procInsId: '',
+  taskId: '',
+  userId: '',
+  copyUserIds: '',
+  nextUserIds: '',
+  vars: '',
+  targetKey: ''
+});
+
+const rules = ref({
+  comment: [{ required: true, message: '璇疯緭鍏ュ鎵规剰瑙�', trigger: 'blur' }]
+});
+
+const queryParams = ref({
+  pageNum: 1,
+  pageSize: 10
+});
+const tagType = (val) => {
+  if (val) {
+      return "success";
+  } else {
+      return "info";
+  }
+}
+const commentType = (val) => {
+  switch (val) {
+    case '1': return '閫氳繃'
+    case '2': return '閫�鍥�'
+    case '3': return '椹冲洖'
+    case '4': return '濮旀淳'
+    case '5': return '杞姙'
+    case '6': return '缁堟'
+    case '7': return '鎾ゅ洖'
+  }
+}
+const approveTypeTag = (val) => {
+  switch (val) {
+    case '1': return 'success'
+    case '2': return 'warning'
+    case '3': return 'danger'
+    case '4': return 'primary'
+    case '5': return 'success'
+    case '6': return 'danger'
+    case '7': return 'info'
+  }
+}
+
+const initData = () => {
+  taskForm.procInsId = route.params && route.params.procInsId ;
+  taskForm.taskId  = route.query && route.query.taskId ;
+  processed.value = route.query && (route.query.processed || false) === "true";
+
+  // 娴佺▼浠诲姟閲嶈幏鍙栧彉閲忚〃鍗�
+  getProcessDetails(taskForm.procInsId, taskForm.taskId);
+  loadIndex.value++;
+};
+/** 閫氳繃鏉′欢杩囨护鑺傜偣  */
+const filterNode = (value, data) => {
+  if (!value) return true
+  return data.label.includes(value)
+}
+ /** 鏍规嵁鍚嶇О绛涢�夐儴闂ㄦ爲 */
+watch(deptName, (val) => {
+  deptTreeRef.value.filter(val)
+})
+// 鑺傜偣鍗曞嚮浜嬩欢
+const handleNodeClick = (data) => {
+  selectDeptId.value=data.id;
+  getList(data.id);
+}
+/** 鏌ヨ閮ㄩ棬涓嬫媺鏍戠粨鏋� */
+const getTreeSelect = async () => {
+  const res = await deptTreeSelect();
+  deptOptions.value = res.data;
+};
+/** 鏌ヨ鐢ㄦ埛鍒楄〃 */
+const getList = async (deptId) => {
+  userLoading.value = true;
+  const res = await selectUser(
+    {
+     deptId: deptId,
+     pageNum:queryParams.value.pageNum,
+     pageSize:queryParams.value.pageSize
+    });
+  userLoading.value = false;
+  userList.value = res.rows;
+  userTotal.value = res.total;
+}
+
+const getProcessDetails = async (procInsId, taskId) => {
+  const params = {procInsId: procInsId, taskId: taskId}
+  const res = await detailProcess(params);
+  const data = res.data;
+  processXml.value = data.bpmnXml;
+  processFormList.value = data.processFormList;
+  taskFormVisible.value = data.existTaskForm;
+  if (taskFormVisible.value) {
+    taskFormData.value = data.taskFormData;
+  }
+  historyProcNodeList.value = data.historyProcNodeList;
+  finishedInfo.value = data.flowViewer;
+  formVisible.value = true;
+  nextTick(() => {
+    processFormList.value.forEach((item, index) => {
+      if (item.disabled) {
+        vFormRenderRef.value[index].disableForm();
+      }
+    })
+  })
+}
+const onSelectCopyUsers = () => {
+  userMultipleSelection.value = copyUser.value;
+  onSelectUsers('娣诲姞鎶勯�佷汉', 'copy')
+}
+const onSelectNextUsers = () => {
+  userMultipleSelection.value = nextUser;
+  onSelectUsers('鎸囧畾瀹℃壒浜�', 'next')
+}
+const onSelectUsers = (title, type) => {
+  userSelectType.value = type;
+  userSelectDialog.title = title;
+  userSelectDialog.visible = true;
+  getTreeSelect();
+  getList()
+}
+/** 閫氳繃浠诲姟 */
+const handleComplete = () => {
+  // 鏍¢獙琛ㄥ崟
+  taskFormRef.value.validate(async (valid) => {
+    if (valid) {
+      const res = await complete(taskForm)
+      proxy.$modal.msgSuccess(res.msg);
+      goBack();
+    }
+  });
+}
+/** 濮旀淳浠诲姟 */
+const handleDelegate = () => {
+  userSelectType.value = 'delegate';
+  userSelectDialog.title = '濮旀淳浠诲姟'
+  userSelectDialog.visible = true;
+  getTreeSelect();
+}
+/** 杞姙浠诲姟 */
+const handleTransfer = () => {
+  userSelectType.value = 'transfer';
+  userSelectDialog.title = '杞姙浠诲姟';
+  userSelectDialog.visible = true;
+  getTreeSelect();
+}
+/** 閫�鍥炰换鍔� */
+const handleReturn = async () => {
+  // 鏍¢獙琛ㄥ崟
+  taskFormRef.value.validate(async (valid) => {
+    if (valid) {
+      const res = await returnList(taskForm);
+      returnTaskList.value = res.data;
+      returnDialog.visible = true;
+    }
+  });
+}
+/** 鎷掔粷浠诲姟 */
+const handleReject = async () => {
+  await proxy.$modal.confirm('鎷掔粷瀹℃壒鍗曟祦绋嬩細缁堟锛屾槸鍚︾户缁�?');
+  await rejectTask(taskForm);
+  proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+  goBack();
+}
+
+/** 杩斿洖椤甸潰 */
+const goBack = () => {
+  // 鍏抽棴褰撳墠鏍囩椤靛苟杩斿洖涓婁釜椤甸潰
+  proxy?.$tab.closePage(route);
+  router.back()
+}
+// 鍏抽棴鏍囩
+const handleClose = (type, tag) => {
+  let userObj = userMultipleSelection.value.find(item => item.userId === tag.id);
+  userMultipleSelection.value.splice(userMultipleSelection.value.indexOf(userObj), 1);
+  if (type === 'copy') {
+    copyUser.value = userMultipleSelection.value;
+    // 璁剧疆鎶勯�佷汉ID
+    if (copyUser.value && copyUser.value.length > 0) {
+      const val = copyUser.value.map(item => item.id);
+      taskForm.copyUserIds = val instanceof Array ? val.join(',') : val;
+    } else {
+      taskForm.copyUserIds = '';
+    }
+  } else if (type === 'next') {
+    nextUser.value = userMultipleSelection.value;
+    // 璁剧疆鎶勯�佷汉ID
+    if (nextUser.value && nextUser.value.length > 0) {
+      const val = nextUser.value.map(item => item.id);
+      taskForm.nextUserIds = val instanceof Array ? val.join(',') : val;
+    } else {
+      taskForm.nextUserIds = '';
+    }
+  }
+}
+const changeCurrentUser = (val) => {
+  // currentUserId = val.userId
+}
+const handleSelectionChange = (val) => {
+  userMultipleSelection.value=val;
+}
+const submitReturn = () => {
+  // 鏍¢獙琛ㄥ崟
+  taskFormRef.value.validate(async (valid) => {
+    if (valid) {
+      if (!returnTaskKey) {
+        proxy.$modal.msgError("璇烽�夋嫨閫�鍥炶妭鐐癸紒");
+      }
+      taskForm.targetKey = returnTaskKey.value;
+      const res = await returnTask(taskForm);
+      proxy.$modal.msgSuccess(res.msg);
+      goBack()
+    }
+  });
+  console.log("taskForm => ", taskForm.targetKey);
+}
+const submitUserData = () => {
+  let type = userSelectType.value;
+  if (type === 'copy' || type === 'next') {
+    if (!userMultipleSelection || userMultipleSelection.value.length <= 0) {
+      proxy.$modal.msgError("璇烽�夋嫨鐢ㄦ埛");
+      return false;
+    }
+    let userIds = userMultipleSelection.value.map(k => k.userId);
+    if (type === 'copy') {
+      // 璁剧疆鎶勯�佷汉ID淇℃伅
+      copyUser.value = userMultipleSelection.value;
+      taskForm.copyUserIds = userIds instanceof Array ? userIds.join(',') : userIds;
+    } else if (type === 'next') {
+      // 璁剧疆涓嬩竴绾у鎵逛汉ID淇℃伅
+      nextUser.value = userMultipleSelection.value;
+      taskForm.nextUserIds = userIds instanceof Array ? userIds.join(',') : userIds;
+    }
+    userSelectDialog.visible = false;
+  } else {
+    if (!taskForm.comment) {
+      proxy?.$modal.msgError("璇疯緭鍏ュ鎵规剰瑙�");
+      return false;
+    }
+    if (!currentUserId.value) {
+      proxy?.$modal.msgError("璇烽�夋嫨鐢ㄦ埛");
+      return false;
+    }
+    taskForm.userId = currentUserId.value;
+    if (type === 'delegate') {
+      delegate(taskForm).then(res => {
+        proxy?.$modal.msgSuccess(res.msg);
+        goBack();
+      });
+    }
+    if (type === 'transfer') {
+      transfer(taskForm).then(res => {
+        proxy?.$modal.msgSuccess(res.msg);
+        goBack();
+      });
+    }
+  }
+}
+
+onMounted(() => {
+  initData();
+});
+</script>
+
+<style lang="scss" scoped>
+.clearfix:before,
+.clearfix:after {
+  display: table;
+  content: "";
+}
+.clearfix:after {
+  clear: both
+}
+
+.box-card {
+  width: 100%;
+  margin-bottom: 20px;
+}
+
+.el-tag + .el-tag {
+  margin-left: 10px;
+}
+
+.el-row {
+  margin-bottom: 20px;
+  &:last-child {
+    margin-bottom: 0;
+  }
+}
+.el-col {
+  border-radius: 4px;
+}
+
+.button-new-tag {
+  margin-left: 10px;
+}
+</style>
diff --git a/ruoyi-ui/apps/web-antd/src/views/workflow/work/finished.vue b/ruoyi-ui/apps/web-antd/src/views/workflow/work/finished.vue
new file mode 100644
index 0000000..540d2ca
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/views/workflow/work/finished.vue
@@ -0,0 +1,140 @@
+<template>
+ <div class="app-container">
+      <div class="search" v-show="showSearch">
+        <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="70">
+          <el-form-item label="娴佺▼鍚嶇О" prop="processName">
+            <el-input v-model="queryParams.processName" placeholder="璇疯緭鍏ユ祦绋嬪悕绉�" clearable style="width: 200px" @keyup.enter="handleQuery" />
+          </el-form-item>
+          <el-form-item label="娴佺▼鍒嗙被" prop="category">
+            <el-select v-model="queryParams.category" clearable placeholder="璇烽�夋嫨"  style="width: 240px">
+              <el-option v-for="item in categoryOptions" :key="item.categoryId" :label="item.categoryName" :value="item.code" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="瀹℃壒鏃堕棿" style="width: 308px;">
+            <el-date-picker
+              v-model="dateRange"
+              value-format="YYYY-MM-DD"
+              type="daterange"
+              range-separator="-"
+              start-placeholder="寮�濮嬫棩鏈�"
+              end-placeholder="缁撴潫鏃ユ湡"
+            ></el-date-picker>
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
+            <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+          </el-form-item>
+        </el-form>
+      </div>
+    <el-card shadow="never">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" :data="finishedList">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="浠诲姟缂栧彿" align="center" prop="taskId" :show-overflow-tooltip="true" />
+        <el-table-column label="娴佺▼鍚嶇О" align="center" prop="procDefName" :show-overflow-tooltip="true" />
+        <el-table-column label="娴佺▼鍒嗙被" align="center" prop="category" :formatter="categoryFormat" />
+        <el-table-column label="浠诲姟鑺傜偣" align="center" prop="taskName" />
+        <el-table-column label="娴佺▼鍙戣捣浜�" align="center" prop="startUserName" />
+        <el-table-column label="鎺ユ敹鏃堕棿" align="center" prop="createTime" width="180" />
+        <el-table-column label="瀹℃壒鏃堕棿" align="center" prop="finishTime" width="180" />
+        <el-table-column label="鑰楁椂" align="center" prop="duration" width="180" />
+        <el-table-column label="鎿嶄綔" width="180" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-tooltip content="璇︽儏" placement="top">
+              <el-button link type="primary" icon="View" @click="handleDetails(scope.row)" v-hasPermi="['workflow:process:query']"></el-button>
+            </el-tooltip>
+            <el-tooltip content="鎾ゅ洖" placement="top">
+              <el-button link type="primary" icon="RefreshLeft" @click="handleRevoke(scope.row)" v-hasPermi="['workflow:process:revoke']"></el-button>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+    </el-card>
+  </div>
+</template>
+
+<script setup name="Finished" lang="js">
+import { listFinishedProcess } from "#/api/workflow/work/process";
+import { revokeProcess } from "#/api/workflow/work/task";
+
+import { listAllCategory } from "#/api/workflow/category";
+const router = useRouter();
+const { proxy } = getCurrentInstance() ;
+
+const finishedList = ref([]);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+const dateRange = ref(['','']);
+const categoryOptions = ref([]);
+const queryFormRef = ref();
+
+const queryParams = ref({
+  pageNum: 1,
+  pageSize: 10,
+  processName: '',
+  category: ''
+});
+/** 鏌ヨ娴佺▼鍒嗙被鍒楄〃 */
+const getCategoryList = async () => {
+  const res = await listAllCategory();
+  categoryOptions.value = res.data;
+}
+/** 鏌ヨ寰呭姙鍒楄〃 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listFinishedProcess(proxy.addDateRange(queryParams.value, dateRange.value));
+  finishedList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+}
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+}
+/** 閲嶇疆鎸夐挳鎿嶄綔 */
+const resetQuery = () => {
+  queryFormRef.value.resetFields();
+  handleQuery();
+}
+/** 娴佺▼娴佽浆璁板綍 */
+const handleDetails = (row) => {
+  router.push({
+    path: '/workflow/process/detail/' + row.procInsId,
+    query: {
+      processed: false
+    }
+  })
+}
+/** 鎾ゅ洖浠诲姟 */
+const handleRevoke = async (row) => {
+  const params = {
+    procInsId: row.procInsId,
+    taskId: row.taskId
+  };
+  const res = await revokeProcess(params);
+  proxy.$modal.msgSuccess(res.msg);
+  getList();
+}
+const categoryFormat = (row) => {
+  var category = categoryOptions.value.find(function(find) {
+        return find.code === row.category;
+    });
+    return category && category.categoryName ? category.categoryName : '';
+}
+onMounted(() => {
+  getCategoryList();
+  getList();
+});
+</script>
diff --git a/ruoyi-ui/apps/web-antd/src/views/workflow/work/index.vue b/ruoyi-ui/apps/web-antd/src/views/workflow/work/index.vue
new file mode 100644
index 0000000..54f6e61
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/views/workflow/work/index.vue
@@ -0,0 +1,158 @@
+<template>
+ <div class="app-container">
+      <div class="search" v-show="showSearch">
+        <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="70">
+          <el-form-item label="娴佺▼鏍囪瘑" prop="processKey">
+            <el-input v-model="queryParams.processKey" placeholder="璇疯緭鍏ユ祦绋嬫爣璇�" clearable style="width: 200px" @keyup.enter="handleQuery" />
+          </el-form-item>
+          <el-form-item label="娴佺▼鍚嶇О" prop="processName">
+            <el-input v-model="queryParams.processName" placeholder="璇疯緭鍏ユ祦绋嬪悕绉�" clearable style="width: 200px" @keyup.enter="handleQuery" />
+          </el-form-item>
+          <el-form-item label="娴佺▼鍒嗙被" prop="category">
+            <el-select v-model="queryParams.category" clearable placeholder="璇烽�夋嫨"  style="width: 240px">
+              <el-option v-for="item in categoryOptions" :key="item.categoryId" :label="item.categoryName" :value="item.code" />
+            </el-select>
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
+            <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+          </el-form-item>
+        </el-form>
+      </div>
+    <el-card shadow="never">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" :data="processList">
+        <el-table-column label="娴佺▼鏍囪瘑" align="center" prop="processKey" :show-overflow-tooltip="true" />
+        <el-table-column label="娴佺▼鍚嶇О" align="center" :show-overflow-tooltip="true">
+          <template #default="scope">
+            <a  @click="handleProcessView(scope.row)" class="link-type">
+              <span>{{ scope.row.processName }}</span>
+            </a>
+          </template>
+        </el-table-column>
+        <el-table-column label="娴佺▼鍒嗙被" align="center" prop="categoryName" :formatter="categoryFormat" />
+        <el-table-column label="娴佺▼鐗堟湰" align="center">
+          <template #default="scope">
+            <el-tag>v{{ scope.row.version }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="鐘舵��" align="center">
+          <template #default="scope">
+            <el-tag type="success" v-if="!scope.row.suspended">婵�娲�</el-tag>
+            <el-tag type="warning" v-if="scope.row.suspended">鎸傝捣</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="閮ㄧ讲鏃堕棿" align="center" prop="deploymentTime" width="180" >
+          <template #default="scope">
+              {{  parseTime(scope.row.deploymentTime, '{y}-{m}-{d}  {h}:{i}:{s}')}}
+          </template>
+        </el-table-column>
+        <el-table-column label="鎿嶄綔" width="180" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-tooltip content="鍙戣捣" placement="top">
+              <el-button link type="primary" icon="VideoPlay" @click="handleStart(scope.row)" v-hasPermi="['workflow:process:start']"></el-button>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+    </el-card>
+
+    <!-- 娴佺▼鍥� -->
+    <el-dialog :title="processDialog.title" v-model="processDialog.visible" width="70%">
+      <ProcessViewer :key="`designer-${reloadIndex}`" :xml="processXml" :style="{height: '650px'}" />
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="WorkProcess" lang="js">
+import { listProcess, getBpmnXml } from "#/api/workflow/work/process";
+import { listAllCategory } from "#/api/workflow/category";
+import ProcessViewer from "#/components/ProcessViewer/index.vue";
+
+const router = useRouter();
+const { proxy } = getCurrentInstance() ;
+
+const processList = ref([]);
+const loading = ref(true);
+const showSearch = ref(true);
+const total = ref(0);
+const reloadIndex = ref(0);
+const processXml = ref("");
+const categoryOptions = ref([]);
+
+const queryFormRef = ref();
+
+const processDialog = reactive({
+  visible: false,
+  title: '娴佺▼鍥�'
+});
+
+const queryParams = ref({
+  pageNum: 1,
+  pageSize: 10,
+  processKey: '',
+  processName: '',
+  category: ''
+});
+
+/** 鏌ヨ娴佺▼鍒嗙被鍒楄〃 */
+const getCategoryList = async () => {
+  const res = await listAllCategory();
+  categoryOptions.value = res.data;
+}
+/** 鏌ヨ娴佺▼鍒楄〃 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listProcess(queryParams.value);
+  processList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+}
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+}
+/** 閲嶇疆鎸夐挳鎿嶄綔 */
+const resetQuery = () => {
+  queryFormRef.value.resetFields();
+  handleQuery();
+}
+/** 鏌ョ湅娴佺▼鍥� */
+const handleProcessView = async (row) => {
+  reloadIndex.value++;
+  // 鍙戦�佽姹傦紝鑾峰彇xml
+  const res = await getBpmnXml(row.definitionId);
+  console.log(res.data);
+  processXml.value = res.data;
+  processDialog.visible = true;
+}
+/** 鍙戣捣娴佺▼ */
+const handleStart = (row) => {
+  router.push({
+    path: '/workflow/process/start/' + row.deploymentId,
+    query: {
+      definitionId: row.definitionId,
+    }
+  })
+};
+
+const categoryFormat = (row) => {
+  var category = categoryOptions.value.find(function(k) {
+        return k.code === row.category;
+    });
+    return category ? category.categoryName : '';
+}
+
+onMounted(() => {
+  getCategoryList();
+  getList();
+});
+</script>
diff --git a/ruoyi-ui/apps/web-antd/src/views/workflow/work/own.vue b/ruoyi-ui/apps/web-antd/src/views/workflow/work/own.vue
new file mode 100644
index 0000000..a3add22
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/views/workflow/work/own.vue
@@ -0,0 +1,193 @@
+<template>
+ <div class="app-container">
+      <div class="search" v-show="showSearch">
+        <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="70">
+          <el-form-item label="娴佺▼鏍囪瘑" prop="processKey">
+            <el-input v-model="queryParams.processKey" placeholder="璇疯緭鍏ユ祦绋嬫爣璇�" clearable style="width: 200px" @keyup.enter="handleQuery" />
+          </el-form-item>
+          <el-form-item label="娴佺▼鍚嶇О" prop="processName">
+            <el-input v-model="queryParams.processName" placeholder="璇疯緭鍏ユ祦绋嬪悕绉�" clearable style="width: 200px" @keyup.enter="handleQuery" />
+          </el-form-item>
+          <el-form-item label="娴佺▼鍒嗙被" prop="category">
+            <el-select v-model="queryParams.category" clearable placeholder="娴佺▼鍒嗙被"  style="width: 240px">
+              <el-option v-for="item in categoryOptions" :key="item.categoryId" :label="item.categoryName" :value="item.code" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="鎻愪氦鏃堕棿" style="width: 308px;">
+            <el-date-picker
+              v-model="dateRange"
+              value-format="YYYY-MM-DD"
+              type="daterange"
+              range-separator="-"
+              start-placeholder="寮�濮嬫棩鏈�"
+              end-placeholder="缁撴潫鏃ユ湡"
+            ></el-date-picker>
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
+            <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+          </el-form-item>
+        </el-form>
+      </div>
+    <el-card shadow="never">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+          <el-col :span="1.5">
+            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['workflow:process:remove']">
+              鍒犻櫎
+            </el-button>
+          </el-col>
+          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" :data="ownProcessList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="娴佺▼缂栧彿" align="center" prop="procInsId" :show-overflow-tooltip="true" />
+        <el-table-column label="娴佺▼鍚嶇О" align="center" prop="procDefName" :show-overflow-tooltip="true" />
+        <el-table-column label="娴佺▼绫诲埆" align="center" prop="category" :formatter="categoryFormat" />
+        <el-table-column label="娴佺▼鐗堟湰" align="center" width="80px">
+          <template #default="scope">
+            <el-tag>v{{ scope.row.procDefVersion }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="褰撳墠鑺傜偣" align="center" prop="taskName" />
+        <el-table-column label="鎻愪氦鏃堕棿" align="center" prop="createTime" width="180" />
+        <el-table-column label="娴佺▼鐘舵��" align="center" width="100">
+          <template #default="scope">
+            <dict-tag :options="wf_process_status" :value="scope.row.processStatus" />
+          </template>
+        </el-table-column>
+        <el-table-column label="鑰楁椂" align="center" prop="duration" width="180" />
+        <el-table-column label="鎿嶄綔" width="180" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-tooltip content="璇︽儏" placement="top">
+              <el-button link type="primary" icon="View" @click="handleDetails(scope.row)" v-hasPermi="['workflow:process:query']"></el-button>
+            </el-tooltip>
+            <el-tooltip content="鍙栨秷" placement="top">
+              <el-button link type="primary" icon="CircleClose" @click="handleStop(scope.row)" v-hasPermi="['workflow:process:cancel']"></el-button>
+            </el-tooltip>
+            <el-tooltip content="鍒犻櫎" placement="top">
+              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['workflow:process:remove']"></el-button>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+    </el-card>
+  </div>
+</template>
+
+<script setup name="Own" lang="js">
+import { listOwnProcess, stopProcess, delProcess } from "#/api/workflow/work/process";
+import { listAllCategory } from "#/api/workflow/category";
+
+
+const router = useRouter();
+const { proxy } = getCurrentInstance() ;
+const { wf_process_status } = proxy.useDict("wf_process_status");
+
+
+
+
+const categoryOptions = ref([]);
+
+const ownProcessList = ref([]);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+const dateRange = ref(['','']);
+
+const queryFormRef = ref();
+
+const queryParams = ref({
+  pageNum: 1,
+  pageSize: 10,
+  processKey: '',
+  processName: '',
+  category: ''
+});
+
+/** 鏌ヨ娴佺▼鍒嗙被鍒楄〃 */
+const getCategoryList = async () => {
+  const res = await listAllCategory();
+  categoryOptions.value = res.data;
+};
+/** 鏌ヨ鎴戠殑娴佺▼鍒楄〃 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listOwnProcess(proxy.addDateRange(queryParams.value, dateRange.value));
+  ownProcessList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+}
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+}
+/** 閲嶇疆鎸夐挳鎿嶄綔 */
+const resetQuery = () => {
+  queryFormRef.value.resetFields();
+  handleQuery();
+}
+/** 澶氶�夋閫変腑鏁版嵁 */
+const handleSelectionChange = (selection) => {
+  ids.value = selection.map(item => item.procInsId);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+}
+/** 娴佺▼璇︽儏 */
+const handleDetails = (row) => {
+  router.push({
+    path: '/workflow/process/detail/' + row.procInsId,
+    query: {
+      processed: false
+    }
+  })
+}
+/** 鍙栨秷娴佺▼鐢宠 */
+const handleStop = async (row) => {
+  await stopProcess( { procInsId: row.procInsId });
+  proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+  getList();
+}
+/** 鍐嶆鍙戣捣娴佺▼ */
+const handleAgain = (row) => {
+  // router.push({
+  //   path: '/workflow/process/start/' + row.deployId,
+  //   query: {
+  //     definitionId: row.procDefId,
+  //     procInsId: row.procInsId
+  //   }
+  // })
+}
+/** 鍒犻櫎鎸夐挳鎿嶄綔 */
+const handleDelete = async (row) => {
+  const procInsIds = row.procInsId || ids.value;
+  await proxy.$modal.confirm('鏄惁纭鍒犻櫎娴佺▼瀹氫箟缂栧彿涓�"' + procInsIds + '"鐨勬暟鎹」?');
+  await delProcess(procInsIds);
+  getList();
+  proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+}
+
+/** 瀵煎嚭鎸夐挳鎿嶄綔 */
+const handleExport = () => {
+  proxy.download("workflow/process/ownExport", {
+    ...queryParams.value
+  }, `own_process_${new Date().getTime()}.xlsx`);
+}
+
+const categoryFormat = (row) => {
+  return categoryOptions.value.find(k => k.code === row.category).categoryName ?categoryOptions.value.find(k => k.code === row.category).categoryName: '';
+}
+
+onMounted(() => {
+  getCategoryList();
+  getList();
+});
+</script>
diff --git a/ruoyi-ui/apps/web-antd/src/views/workflow/work/start.vue b/ruoyi-ui/apps/web-antd/src/views/workflow/work/start.vue
new file mode 100644
index 0000000..73a2839
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/views/workflow/work/start.vue
@@ -0,0 +1,75 @@
+<template>
+  <div class="app-container">
+    <el-card class="box-card">
+      <template #header>
+        <span>鍙戣捣娴佺▼</span>
+      </template>
+      <div class="form-conf" v-if="dialog.visible">
+        <v-form-render :form-json="formModel" :form-data="formData" ref="vfRenderRef"></v-form-render>
+        <div class="cu-submit">
+          <el-button type="primary" @click="submit">鎻愪氦</el-button>
+          <el-button @click="reset">閲嶇疆</el-button>
+        </div>
+      </div>
+    </el-card>
+  </div>
+</template>
+
+<script setup name="WorkStart" lang="js">
+import { getProcessForm, startProcess } from '#/api/workflow/work/process';
+
+
+const route = useRoute();
+const { proxy } = getCurrentInstance() ;
+
+const vfRenderRef = ref(null);
+
+const deployId = ref();
+const definitionId = ref();
+const formModel = ref({});
+const formData = ref({});
+
+const dialog = reactive({
+  visible: false,
+  title: ''
+});
+
+const initData = async () => {
+  deployId.value = route.params && route.params.deployId;
+  definitionId.value = route.query && route.query.definitionId;
+  const res = await getProcessForm({ definitionId: definitionId.value, deployId: deployId.value });
+  formModel.value = res.data.formModel;
+  dialog.visible = true;
+  nextTick(async () => {
+    vfRenderRef.value.setFormJson(formModel.value || {formConfig: {}, widgetList: []});
+  });
+}
+
+const submit = async () => {
+  const data = await vfRenderRef.value.getFormData();
+  if (definitionId.value) {
+    const res = await startProcess(definitionId.value, JSON.stringify(data));
+    proxy.$modal.msgSuccess(res.msg);
+    // const obj = { path: "/work/own" };
+    // proxy?.$tab.closeOpenPage(obj);
+    proxy.$tab.closePage();
+    proxy.$router.back();
+  }
+}
+
+const reset = () => {
+  vfRenderRef.value.resetForm();
+}
+
+onMounted(() => {
+  initData();
+});
+</script>
+
+<style lang="scss" scoped>
+.form-conf {
+  margin: 15px auto;
+  width: 80%;
+  padding: 15px;
+}
+</style>
diff --git a/ruoyi-ui/apps/web-antd/src/views/workflow/work/todo.vue b/ruoyi-ui/apps/web-antd/src/views/workflow/work/todo.vue
new file mode 100644
index 0000000..092175d
--- /dev/null
+++ b/ruoyi-ui/apps/web-antd/src/views/workflow/work/todo.vue
@@ -0,0 +1,132 @@
+<template>
+ <div class="app-container">
+      <div class="search" v-show="showSearch">
+        <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="70">
+          <el-form-item label="娴佺▼鍚嶇О" prop="processName">
+            <el-input v-model="queryParams.processName" placeholder="璇疯緭鍏ユ祦绋嬪悕绉�" clearable style="width: 200px" @keyup.enter="handleQuery" />
+          </el-form-item>
+          <el-form-item label="娴佺▼鍒嗙被" prop="category">
+            <el-select v-model="queryParams.category" clearable placeholder="璇烽�夋嫨"  style="width: 240px">
+              <el-option v-for="item in categoryOptions" :key="item.categoryId" :label="item.categoryName" :value="item.code" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="鎺ユ敹鏃堕棿" style="width: 308px;">
+            <el-date-picker
+              v-model="dateRange"
+              value-format="YYYY-MM-DD"
+              type="daterange"
+              range-separator="-"
+              start-placeholder="寮�濮嬫棩鏈�"
+              end-placeholder="缁撴潫鏃ユ湡"
+            ></el-date-picker>
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
+            <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
+          </el-form-item>
+        </el-form>
+      </div>
+    <el-card shadow="never">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" :data="todoList">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="浠诲姟缂栧彿" align="center" prop="taskId" :show-overflow-tooltip="true" />
+        <el-table-column label="娴佺▼鍚嶇О" align="center" prop="procDefName" />
+        <el-table-column label="浠诲姟鑺傜偣" align="center" prop="taskName" />
+        <el-table-column label="娴佺▼鍒嗙被" align="center" prop="category" :formatter="categoryFormat" />
+        <el-table-column label="娴佺▼鐗堟湰" align="center">
+          <template #default="scope">
+            <el-tag>v{{scope.row.procDefVersion}}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="娴佺▼鍙戣捣浜�" align="center" prop="startUserName" />
+        <el-table-column label="鎺ユ敹鏃堕棿" align="center" prop="createTime" width="180" />
+        <el-table-column label="鎿嶄綔" width="180" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-tooltip content="鍔炵悊" placement="top">
+              <el-button link type="primary" icon="EditPen" @click="handleProcess(scope.row)" v-hasPermi="['workflow:process:approval']"></el-button>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+    </el-card>
+  </div>
+</template>
+
+<script setup name="Todo" lang="js">
+import { listTodoProcess } from '#/api/workflow/work/process';
+
+import { listAllCategory } from "#/api/workflow/category";
+const router = useRouter();
+const { proxy } = getCurrentInstance() ;
+
+const todoList = ref([]);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+const dateRange = ref(['','']);
+const categoryOptions = ref([]);
+const queryFormRef = ref();
+
+const queryParams = ref({
+  pageNum: 1,
+  pageSize: 10,
+  processName: '',
+  category: ''
+});
+/** 鏌ヨ娴佺▼鍒嗙被鍒楄〃 */
+const getCategoryList = async () => {
+  const res = await listAllCategory();
+  categoryOptions.value = res.data;
+}
+/** 鏌ヨ寰呭姙鍒楄〃 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listTodoProcess(proxy.addDateRange(queryParams.value, dateRange.value));
+  todoList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+}
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+}
+/** 閲嶇疆鎸夐挳鎿嶄綔 */
+const resetQuery = () => {
+  queryFormRef.value.resetFields();
+  handleQuery();
+}
+/** 璺宠浆鍒板鐞嗛〉闈� */
+const handleProcess = (row) => {
+  router.push({
+    path: '/workflow/process/detail/' + row.procInsId,
+    query: {
+      taskId: row.taskId,
+      processed: true
+    }
+  })
+}
+
+const categoryFormat = (row) => {
+  var category = categoryOptions.value.find(function(k) {
+        return k.code === row.category;
+    });
+    return category && category.categoryName ? category.categoryName : '';
+}
+
+onMounted(() => {
+  getCategoryList();
+  getList();
+});
+</script>

--
Gitblit v1.9.3