From a63543a5c793c8954fa2f9da0ee4fb215c62d8c2 Mon Sep 17 00:00:00 2001
From: 疯狂的狮子Li <15040126243@163.com>
Date: 星期一, 20 五月 2024 10:26:46 +0800
Subject: [PATCH] !118 ♥️发布 5.2.0-BETA 公测版本 Merge pull request !118 from 疯狂的狮子Li/dev

---
 src/assets/styles/index.scss                                       |    9 
 src/layout/index.vue                                               |   70 
 src/types/bpmn/index.d.ts                                          |   15 
 src/views/system/tenant/index.vue                                  |  136 
 src/views/system/user/authRole.vue                                 |   42 
 src/views/monitor/operlog/index.vue                                |   79 
 src/assets/icons/svg/model.svg                                     |    1 
 src/bpmn/assets/moddle/flowable.ts                                 | 1250 +++
 src/types/bpmn/editor/global.d.ts                                  |   13 
 src/layout/components/TopBar/search.vue                            |  188 
 src/components/LangSelect/index.vue                                |   11 
 src/bpmn/index.vue                                                 |  496 +
 src/views/workflow/processDefinition/index.vue                     |  517 +
 src/components/BuildCode/index.vue                                 |   73 
 src/views/monitor/cache/index.vue                                  |   77 
 src/views/workflow/leave/index.vue                                 |  251 
 src/layout/components/notice/index.vue                             |   23 
 src/store/modules/notice.ts                                        |    2 
 src/views/error/404.vue                                            |    8 
 src/api/system/dept/types.ts                                       |    3 
 src/bpmn/hooks/useParseElement.ts                                  |   34 
 src/components/SizeSelect/index.vue                                |   14 
 src/views/system/oss/index.vue                                     |  147 
 src/components/Breadcrumb/index.vue                                |   41 
 .env.development                                                   |   10 
 src/bpmn/assets/defaultXML.ts                                      |   23 
 src/directive/permission/index.ts                                  |    4 
 vite/plugins/compression.ts                                        |    4 
 src/components/TopNav/index.vue                                    |  105 
 src/assets/icons/svg/waiting.svg                                   |    1 
 src/assets/icons/svg/category.svg                                  |    1 
 src/views/system/role/index.vue                                    |  260 
 src/types/element.d.ts                                             |   35 
 src/views/system/oss/config.vue                                    |  179 
 src/api/workflow/processDefinition/types.ts                        |   24 
 src/assets/styles/variables.module.scss                            |   28 
 vite.config.ts                                                     |   28 
 src/views/error/401.vue                                            |    4 
 src/api/workflow/processDefinition/index.ts                        |  114 
 src/views/system/dict/data.vue                                     |  146 
 src/views/tool/gen/editTable.vue                                   |   43 
 src/api/system/post/types.ts                                       |    8 
 src/views/system/user/profile/onlineDevice.vue                     |   59 
 src/views/tool/gen/importTable.vue                                 |   42 
 src/api/system/post/index.ts                                       |   12 
 src/bpmn/panel/ParticipantPanel.vue                                |   68 
 .eslintrc.cjs                                                      |   23 
 src/views/system/dept/index.vue                                    |  146 
 src/store/modules/user.ts                                          |    4 
 src/bpmn/panel/property/DueDate.vue                                |  252 
 src/main.ts                                                        |   40 
 src/utils/index.ts                                                 |    2 
 src/plugins/index.ts                                               |   19 
 .prettierrc                                                        |   20 
 src/views/system/user/profile/userInfo.vue                         |   48 
 src/components/iFrame/index.vue                                    |   12 
 src/api/tool/gen/index.ts                                          |    4 
 src/api/workflow/nodeConfig/index.ts                               |   63 
 src/api/workflow/formManage/types.ts                               |   69 
 src/views/system/user/profile/thirdParty.vue                       |   51 
 src/views/register.vue                                             |   70 
 src/api/workflow/nodeConfig/types.ts                               |   43 
 src/views/workflow/processInstance/index.vue                       |  361 +
 src/layout/components/Sidebar/Logo.vue                             |   26 
 index.html                                                         |   31 
 src/types/router.d.ts                                              |   54 
 src/views/tool/gen/basicInfoForm.vue                               |   22 
 src/api/workflow/formManage/index.ts                               |   76 
 src/api/workflow/task/types.ts                                     |   49 
 src/components/Pagination/index.vue                                |   94 
 src/views/workflow/model/index.vue                                 |  383 +
 src/lang/index.ts                                                  |   22 
 src/bpmn/assets/lang/zh.ts                                         |  126 
 tsconfig.json                                                      |   18 
 src/components/FileUpload/index.vue                                |  242 
 src/views/system/dict/index.vue                                    |   95 
 src/types/bpmn/moddle.d.ts                                         |   37 
 .gitignore                                                         |    1 
 src/bpmn/assets/module/ContextPad/CustomContextPadProvider.ts      |  138 
 src/layout/components/SocialCallback/index.vue                     |    5 
 src/api/workflow/category/types.ts                                 |   67 
 src/types/global.d.ts                                              |   77 
 src/hooks/useDialog.ts                                             |   31 
 src/assets/icons/svg/workflow.svg                                  |    1 
 src/views/redirect/index.vue                                       |    8 
 src/api/workflow/category/index.ts                                 |   63 
 src/lang/zh_CN.json                                                |   25 
 src/utils/websocket.ts                                             |    8 
 src/views/system/role/selectUser.vue                               |   38 
 src/components/HeaderSearch/index.vue                              |  100 
 src/components/SvgIcon/index.vue                                   |   20 
 src/components/RightToolbar/index.vue                              |   50 
 src/views/monitor/online/index.vue                                 |   34 
 src/views/workflow/task/taskCopyList.vue                           |  150 
 src/bpmn/assets/module/Palette/CustomPaletteProvider.ts            |  109 
 src/types/module.d.ts                                              |   29 
 src/components/BpmnView/index.vue                                  |  410 +
 src/components/IconSelect/index.vue                                |   26 
 src/views/system/user/index.vue                                    |  337 
 src/views/workflow/task/taskWaiting.vue                            |  149 
 src/views/login.vue                                                |   63 
 src/views/tool/gen/genInfoForm.vue                                 |   66 
 src/layout/components/IframeToggle/index.vue                       |   27 
 src/types/bpmn/panel.d.ts                                          |   92 
 package.json                                                       |  133 
 src/components/RuoYiGit/index.vue                                  |    2 
 src/bpmn/panel/SubProcessPanel.vue                                 |  193 
 src/App.vue                                                        |   13 
 src/assets/icons/svg/process-definition.svg                        |    1 
 src/bpmn/panel/property/TaskListener.vue                           |  310 
 src/bpmn/assets/style/index.scss                                   |  284 
 src/assets/icons/svg/caret-back.svg                                |    1 
 src/bpmn/panel/property/ExecutionListener.vue                      |  308 
 src/api/workflow/definitionConfig/types.ts                         |  102 
 src/views/monitor/logininfor/index.vue                             |  144 
 src/enums/bpmn/IndexEnums.ts                                       |   17 
 html/ie.html                                                       |  216 
 src/store/modules/permission.ts                                    |   90 
 src/views/workflow/leave/leaveEdit.vue                             |  273 
 src/components/BpmnDesign/index.vue                                |   71 
 src/components/Process/approvalRecord.vue                          |  116 
 src/views/system/tenantPackage/index.vue                           |   95 
 src/settings.ts                                                    |    9 
 src/components/ImageUpload/index.vue                               |  256 
 vite/plugins/i18n.ts                                               |    6 
 src/views/workflow/task/myDocument.vue                             |  261 
 src/api/workflow/model/types.ts                                    |   66 
 vite/plugins/setup-extend.ts                                       |    4 
 uno.config.ts                                                      |   33 
 src/lang/en_US.json                                                |   25 
 src/bpmn/panel/TaskPanel.vue                                       |  492 +
 src/layout/components/Sidebar/Link.vue                             |   44 
 src/components/Hamburger/index.vue                                 |    8 
 src/views/tool/gen/index.vue                                       |   87 
 src/store/modules/modeler.ts                                       |   76 
 src/components/ImagePreview/index.vue                              |   23 
 src/components/Process/submitVerify.vue                            |  353 +
 src/bpmn/assets/module/Translate/index.ts                          |   15 
 src/api/workflow/model/index.ts                                    |  104 
 src/assets/icons/svg/my-task.svg                                   |    1 
 src/components/Editor/index.vue                                    |  187 
 src/api/workflow/task/index.ts                                     |  264 
 src/views/system/menu/index.vue                                    |  144 
 src/views/workflow/task/allTaskWaiting.vue                         |  286 
 src/layout/components/Sidebar/index.vue                            |   44 
 vite/plugins/index.ts                                              |   22 
 src/bpmn/hooks/usePanel.ts                                         |  145 
 src/components/RuoYiDoc/index.vue                                  |    2 
 src/assets/styles/sidebar.scss                                     |    6 
 src/store/modules/tagsView.ts                                      |   86 
 src/api/login.ts                                                   |    6 
 src/assets/styles/element-ui.scss                                  |   35 
 src/layout/components/TagsView/index.vue                           |  364 
 src/types/axios.d.ts                                               |    5 
 src/assets/icons/svg/finish.svg                                    |    1 
 src/api/workflow/definitionConfig/index.ts                         |   49 
 src/components/UserSelect/index.vue                                |  314 
 src/permission.ts                                                  |   13 
 src/components/TreeSelect/index.vue                                |  123 
 src/bpmn/panel/SequenceFlowPanel.vue                               |   95 
 src/views/system/user/profile/resetPwd.vue                         |   45 
 src/components/Process/multiInstanceUser.vue                       |  368 +
 src/layout/components/AppMain.vue                                  |   31 
 src/utils/ruoyi.ts                                                 |    6 
 .eslintignore                                                      |    4 
 src/bpmn/assets/showConfig.ts                                      |   50 
 src/assets/icons/svg/caret-forward.svg                             |    1 
 src/bpmn/panel/StartEndPanel.vue                                   |   67 
 src/views/index.vue                                                |   21 
 src/assets/icons/svg/topiam.svg                                    |   29 
 src/components/BuildCode/render.vue                                |   69 
 src/store/modules/app.ts                                           |    4 
 src/api/system/client/types.ts                                     |    2 
 src/views/system/user/profile/userAvatar.vue                       |   49 
 src/views/workflow/category/index.vue                              |  263 
 src/views/workflow/formManage/index.vue                            |  243 
 src/api/workflow/workflowCommon/index.ts                           |   29 
 src/api/system/client/index.ts                                     |    6 
 src/directive/common/copyText.ts                                   |    3 
 src/assets/icons/svg/my-copy.svg                                   |    1 
 src/layout/components/InnerLink/index.vue                          |   15 
 vite/plugins/unocss.ts                                             |    7 
 src/views/system/role/authUser.vue                                 |   72 
 src/bpmn/assets/module/index.ts                                    |   17 
 src/plugins/tab.ts                                                 |   34 
 src/bpmn/panel/GatewayPanel.vue                                    |   68 
 src/router/index.ts                                                |   20 
 src/views/system/post/index.vue                                    |  371 
 src/utils/request.ts                                               |   51 
 src/views/system/user/profile/index.vue                            |   71 
 src/types/env.d.ts                                                 |   59 
 src/api/workflow/workflowCommon/types.ts                           |   16 
 src/enums/LanguageEnum.ts                                          |    5 
 README.md                                                          |  101 
 src/bpmn/assets/module/Renderer/CustomRenderer.ts                  |   56 
 src/bpmn/panel/ProcessPanel.vue                                    |   71 
 src/api/system/config/index.ts                                     |    2 
 src/layout/components/TagsView/ScrollPane.vue                      |  119 
 .env.production                                                    |    6 
 src/views/system/notice/index.vue                                  |   82 
 src/layout/components/Settings/index.vue                           |  174 
 src/api/workflow/leave/types.ts                                    |   24 
 src/views/system/config/index.vue                                  |  102 
 src/api/workflow/processInstance/index.ts                          |  136 
 src/layout/components/Navbar.vue                                   |   94 
 src/api/monitor/online/index.ts                                    |   16 
 src/views/workflow/task/taskFinish.vue                             |  136 
 src/api/workflow/processInstance/types.ts                          |   27 
 src/store/modules/settings.ts                                      |   47 
 src/views/monitor/snailjob/index.vue                               |    2 
 src/layout/components/Sidebar/SidebarItem.vue                      |  121 
 src/views/system/client/index.vue                                  |  134 
 src/api/system/user/types.ts                                       |    3 
 src/components/DictTag/index.vue                                   |   81 
 src/views/demo/tree/index.vue                                      |  125 
 src/bpmn/panel/index.vue                                           |  110 
 src/api/system/user/index.ts                                       |   18 
 src/bpmn/panel/property/ListenerParam.vue                          |  121 
 src/api/workflow/leave/index.ts                                    |   63 
 /dev/null                                                          |    4 
 src/components/RoleSelect/index.vue                                |  250 
 src/utils/propTypes.ts                                             |    1 
 src/views/demo/demo/index.vue                                      |  114 
 src/api/system/role/index.ts                                       |   16 
 src/views/workflow/processDefinition/components/processPreview.vue |   45 
 src/api/system/tenant/index.ts                                     |    3 
 226 files changed, 16,511 insertions(+), 3,737 deletions(-)

diff --git a/.env.development b/.env.development
index 7edd98a..e1f7af6 100644
--- a/.env.development
+++ b/.env.development
@@ -13,11 +13,13 @@
 # 鐩戞帶鍦板潃
 VITE_APP_MONITRO_ADMIN = 'http://localhost:9090/admin/applications'
 
-# powerjob 鎺у埗鍙板湴鍧�
-VITE_APP_POWERJOB_ADMIN = 'http://localhost:7700/'
+# SnailJob 鎺у埗鍙板湴鍧�
+VITE_APP_SNAILJOB_ADMIN = 'http://localhost:8800/snail-job'
 
 VITE_APP_PORT = 80
 
+# 鎺ュ彛鍔犲瘑鍔熻兘寮�鍏�(濡傞渶鍏抽棴 鍚庣涔熷繀椤诲搴斿叧闂�)
+VITE_APP_ENCRYPT = true
 # 鎺ュ彛鍔犲瘑浼犺緭 RSA 鍏挜涓庡悗绔В瀵嗙閽ュ搴� 濡傛洿鎹㈤渶鍓嶅悗绔竴鍚屾洿鎹�
 VITE_APP_RSA_PUBLIC_KEY = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ=='
 # 鎺ュ彛鍝嶅簲瑙e瘑 RSA 绉侀挜涓庡悗绔姞瀵嗗叕閽ュ搴� 濡傛洿鎹㈤渶鍓嶅悗绔竴鍚屾洿鎹�
@@ -26,5 +28,5 @@
 # 瀹㈡埛绔痠d
 VITE_APP_CLIENT_ID = 'e5cd7e4891bf95d1d19206ce24a7b32e'
 
-# websocket 寮�鍏�(寮�鍙戠幆澧冮粯璁ゅ叧闂瓀s 鍥爒ite鐨刡ug瀵艰嚧濡倃s鏃犳硶杩炴帴鍒欎細宕╂簝)
-VITE_APP_WEBSOCKET = false
+# websocket 寮�鍏�
+VITE_APP_WEBSOCKET = true
diff --git a/.env.production b/.env.production
index 0e3b9fc..2700ee0 100644
--- a/.env.production
+++ b/.env.production
@@ -10,8 +10,8 @@
 # 鐩戞帶鍦板潃
 VITE_APP_MONITRO_ADMIN = '/admin/applications'
 
-# powerjob 鎺у埗鍙板湴鍧�
-VITE_APP_POWERJOB_ADMIN = '/powerjob'
+# SnailJob 鎺у埗鍙板湴鍧�
+VITE_APP_SNAILJOB_ADMIN = 'http://localhost:8800/snail-job'
 
 # 鐢熶骇鐜
 VITE_APP_BASE_API = '/prod-api'
@@ -21,6 +21,8 @@
 
 VITE_APP_PORT = 80
 
+# 鎺ュ彛鍔犲瘑鍔熻兘寮�鍏�(濡傞渶鍏抽棴 鍚庣涔熷繀椤诲搴斿叧闂�)
+VITE_APP_ENCRYPT = true
 # 鎺ュ彛鍔犲瘑浼犺緭 RSA 鍏挜涓庡悗绔В瀵嗙閽ュ搴� 濡傛洿鎹㈤渶鍓嶅悗绔竴鍚屾洿鎹�
 VITE_APP_RSA_PUBLIC_KEY = 'MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKoR8mX0rGKLqzcWmOzbfj64K8ZIgOdHnzkXSOVOZbFu/TJhZ7rFAN+eaGkl3C4buccQd/EjEsj9ir7ijT7h96MCAwEAAQ=='
 # 鎺ュ彛鍝嶅簲瑙e瘑 RSA 绉侀挜涓庡悗绔姞瀵嗗叕閽ュ搴� 濡傛洿鎹㈤渶鍓嶅悗绔竴鍚屾洿鎹�
diff --git a/.eslintignore b/.eslintignore
index c4d771a..e74db40 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -11,7 +11,7 @@
 .husky
 .local
 /bin
-.eslintrc.js
+.eslintrc.cjs
 prettier.config.js
 src/assets
-tailwind.config.js
\ No newline at end of file
+tailwind.config.js
diff --git a/.eslintrc.js b/.eslintrc.cjs
similarity index 65%
rename from .eslintrc.js
rename to .eslintrc.cjs
index a1b32e3..6042c39 100644
--- a/.eslintrc.js
+++ b/.eslintrc.cjs
@@ -1,28 +1,37 @@
 module.exports = {
   env: {
     browser: true,
-    es2021: true,
-    node: true
+    node: true,
+    es6: true
   },
   parser: 'vue-eslint-parser',
   extends: [
-    'eslint:recommended',
-    'plugin:vue/vue3-essential',
-    'plugin:@typescript-eslint/recommended',
+    'plugin:vue/vue3-recommended',
     './.eslintrc-auto-import.json',
+    'plugin:@typescript-eslint/recommended',
+    'prettier',
     'plugin:prettier/recommended'
   ],
   parserOptions: {
     ecmaVersion: '2020',
     sourceType: 'module',
+    project: './tsconfig.*?.json',
     parser: '@typescript-eslint/parser'
   },
-  plugins: ['vue', '@typescript-eslint'],
+  plugins: ['vue', '@typescript-eslint', 'import', 'promise', 'node', 'prettier'],
   rules: {
-    'vue/multi-word-component-names': 'off',
     '@typescript-eslint/no-empty-function': 'off',
     '@typescript-eslint/no-explicit-any': 'off',
+    '@typescript-eslint/no-unused-vars': 'off',
+    '@typescript-eslint/no-this-alias': 'off',
+
+    // vue
+    'vue/multi-word-component-names': 'off',
+    'vue/valid-define-props': 'off',
     'vue/no-v-model-argument': 'off',
+    'prefer-rest-params': 'off',
+    // prettier
+    'prettier/prettier': 'error',
     '@typescript-eslint/ban-types': [
       'error',
       {
diff --git a/.gitignore b/.gitignore
index 40df474..1fd56f0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,6 +22,7 @@
 
 package-lock.json
 yarn.lock
+pnpm-lock.yaml
 
 # 缂栬瘧鐢熸垚鐨勬枃浠�
 auto-imports.d.ts
diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 0000000..6ca3ce5
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,20 @@
+{
+  "printWidth": 150,
+  "tabWidth": 2,
+  "useTabs": false,
+  "semi": true,
+  "singleQuote": true,
+  "quoteProps": "preserve",
+  "jsxSingleQuote": false,
+  "bracketSameLine": false,
+  "trailingComma": "none",
+  "bracketSpacing": true,
+  "embeddedLanguageFormatting": "auto",
+  "arrowParens": "always",
+  "requirePragma": false,
+  "insertPragma": false,
+  "proseWrap": "preserve",
+  "htmlWhitespaceSensitivity": "css",
+  "vueIndentScriptAndStyle": false,
+  "endOfLine": "auto"
+}
diff --git a/.prettierrc.cjs b/.prettierrc.cjs
deleted file mode 100644
index 61d2d92..0000000
--- a/.prettierrc.cjs
+++ /dev/null
@@ -1,46 +0,0 @@
-/**
- * 浠g爜鏍煎紡鍖栭厤缃�
- */
-module.exports = {
-  // 涓�琛屾渶澶氬灏戜釜瀛楃
-  printWidth: 150,
-  // 鎸囧畾姣忎釜缂╄繘绾у埆鐨勭┖鏍兼暟
-  tabWidth: 2,
-  // 浣跨敤鍒惰〃绗﹁�屼笉鏄┖鏍肩缉杩涜
-  useTabs: false,
-  // 鍦ㄨ鍙ユ湯灏炬槸鍚﹂渶瑕佸垎鍙�
-  semi: true,
-  // 鏄惁浣跨敤鍗曞紩鍙�
-  singleQuote: true,
-  // 鏇存敼寮曠敤瀵硅薄灞炴�х殑鏃堕棿 鍙�夊��"<as-needed|consistent|preserve>"
-  quoteProps: 'as-needed',
-  // 鍦↗SX涓娇鐢ㄥ崟寮曞彿鑰屼笉鏄弻寮曞彿
-  jsxSingleQuote: false,
-  // 澶氳鏃跺敖鍙兘鎵撳嵃灏鹃殢閫楀彿銆傦紙渚嬪锛屽崟琛屾暟缁勬案杩滀笉浼氬嚭鐜伴�楀彿缁撳熬銆傦級 鍙�夊��"<none|es5|all>"锛岄粯璁one
-  trailingComma: 'none',
-  // 鍦ㄥ璞℃枃瀛椾腑鐨勬嫭鍙蜂箣闂存墦鍗扮┖鏍�
-  bracketSpacing: true,
-  // jsx 鏍囩鐨勫弽灏栨嫭鍙烽渶瑕佹崲琛�
-  jsxBracketSameLine: false,
-  embeddedLanguageFormatting: 'off',
-  // 鍦ㄥ崟鐙殑绠ご鍑芥暟鍙傛暟鍛ㄥ洿鍖呮嫭鎷彿 always锛�(x) => x \ avoid锛歺 => x
-  arrowParens: 'always',
-  // 杩欎袱涓�夐」鍙敤浜庢牸寮忓寲浠ョ粰瀹氬瓧绗﹀亸绉婚噺锛堝垎鍒寘鎷拰涓嶅寘鎷級寮�濮嬪拰缁撴潫鐨勪唬鐮�
-  rangeStart: 0,
-  rangeEnd: Infinity,
-  // 鎸囧畾瑕佷娇鐢ㄧ殑瑙f瀽鍣紝涓嶉渶瑕佸啓鏂囦欢寮�澶寸殑 @prettier
-  requirePragma: false,
-  // 涓嶉渶瑕佽嚜鍔ㄥ湪鏂囦欢寮�澶存彃鍏� @prettier
-  insertPragma: false,
-  // 浣跨敤榛樿鐨勬姌琛屾爣鍑� always\never\preserve
-  proseWrap: 'preserve',
-  // 鎸囧畾HTML鏂囦欢鐨勫叏灞�绌烘牸鏁忔劅搴� css\strict\ignore
-  htmlWhitespaceSensitivity: 'css',
-  // Vue鏂囦欢鑴氭湰鍜屾牱寮忔爣绛剧缉杩�
-  vueIndentScriptAndStyle: false,
-  // 鍦� windows 鎿嶄綔绯荤粺涓崲琛岀閫氬父鏄洖杞� (CR) 鍔犳崲琛屽垎闅旂 (LF)锛屼篃灏辨槸鍥炶溅鎹㈣(CRLF)锛�
-  // 鐒惰�屽湪 Linux 鍜� Unix 涓彧浣跨敤绠�鍗曠殑鎹㈣鍒嗛殧绗� (LF)銆�
-  // 瀵瑰簲鐨勬帶鍒跺瓧绗︿负 "\n" (LF) 鍜� "\r\n"(CRLF)銆俛uto鎰忎负淇濇寔鐜版湁鐨勮灏�
-  // 鎹㈣绗︿娇鐢� lf 缁撳熬鏄� 鍙�夊��"<auto|lf|crlf|cr>"
-  endOfLine: 'auto'
-};
diff --git a/README.md b/README.md
index 2742463..010adb9 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,10 @@
 ## 骞冲彴绠�浠�
 
-* 鏈粨搴撲负鍓嶇鎶�鏈爤 [Vue3](https://v3.cn.vuejs.org) + [TS](https://www.typescriptlang.org/) + [Element Plus](https://element-plus.org/zh-CN) + [Vite](https://cn.vitejs.dev) 鐗堟湰銆�
-* 閰嶅鍚庣浠g爜浠撳簱鍦板潃
-* [RuoYi-Vue-Plus 5.X(娉ㄦ剰鐗堟湰鍙�)](https://gitee.com/dromara/RuoYi-Vue-Plus)
-* [RuoYi-Cloud-Plus 2.X(娉ㄦ剰鐗堟湰鍙�)](https://gitee.com/dromara/RuoYi-Cloud-Plus)
+- 鏈粨搴撲负鍓嶇鎶�鏈爤 [Vue3](https://v3.cn.vuejs.org) + [TS](https://www.typescriptlang.org/) + [Element Plus](https://element-plus.org/zh-CN) + [Vite](https://cn.vitejs.dev) 鐗堟湰銆�
+- 鎴愬憳椤圭洰: 鍩轰簬 vben(ant-design-vue) 鐨勫墠绔」鐩� [ruoyi-plus-vben](https://gitee.com/dapppp/ruoyi-plus-vben)
+- 閰嶅鍚庣浠g爜浠撳簱鍦板潃
+- [RuoYi-Vue-Plus 5.X(娉ㄦ剰鐗堟湰鍙�)](https://gitee.com/dromara/RuoYi-Vue-Plus)
+- [RuoYi-Cloud-Plus 2.X(娉ㄦ剰鐗堟湰鍙�)](https://gitee.com/dromara/RuoYi-Cloud-Plus)
 
 ## 鍓嶇杩愯
 
@@ -17,7 +18,7 @@
 # 鍚姩鏈嶅姟
 npm run dev
 
-# 鏋勫缓鐢熶骇鐜 
+# 鏋勫缓鐢熶骇鐜
 npm run build:prod
 
 # 鍓嶇璁块棶鍦板潃 http://localhost:80
@@ -25,51 +26,51 @@
 
 ## 鏈鏋朵笌RuoYi鐨勪笟鍔″樊寮�
 
-| 涓氬姟     | 鍔熻兘璇存槑                                    | 鏈鏋� | RuoYi            |
-|--------|-----------------------------------------|-----|------------------|
-| 绉熸埛绠$悊   | 绯荤粺鍐呯鎴风殑绠$悊 濡�:绉熸埛濂楅銆佽繃鏈熸椂闂淬�佺敤鎴锋暟閲忋�佷紒涓氫俊鎭瓑         | 鏀寔  | 鏃�                |
-| 绉熸埛濂楅绠$悊 | 绯荤粺鍐呯鎴锋墍鑳戒娇鐢ㄧ殑濂楅绠$悊 濡�:濂楅鍐呮墍鍖呭惈鐨勮彍鍗曠瓑             | 鏀寔  | 鏃�                |
-| 鐢ㄦ埛绠$悊   | 鐢ㄦ埛鐨勭鐞嗛厤缃� 濡�:鏂板鐢ㄦ埛銆佸垎閰嶇敤鎴锋墍灞為儴闂ㄣ�佽鑹层�佸矖浣嶇瓑          | 鏀寔  | 鏀寔               |
-| 閮ㄩ棬绠$悊   | 閰嶇疆绯荤粺缁勭粐鏈烘瀯锛堝叕鍙搞�侀儴闂ㄣ�佸皬缁勶級 鏍戠粨鏋勫睍鐜版敮鎸佹暟鎹潈闄�          | 鏀寔  | 鏀寔               |
-| 宀椾綅绠$悊   | 閰嶇疆绯荤粺鐢ㄦ埛鎵�灞炴媴浠昏亴鍔�                            | 鏀寔  | 鏀寔               |
-| 鑿滃崟绠$悊   | 閰嶇疆绯荤粺鑿滃崟銆佹搷浣滄潈闄愩�佹寜閽潈闄愭爣璇嗙瓑                     | 鏀寔  | 鏀寔               |
-| 瑙掕壊绠$悊   | 瑙掕壊鑿滃崟鏉冮檺鍒嗛厤銆佽缃鑹叉寜鏈烘瀯杩涜鏁版嵁鑼冨洿鏉冮檺鍒掑垎              | 鏀寔  | 鏀寔               |
-| 瀛楀吀绠$悊   | 瀵圭郴缁熶腑缁忓父浣跨敤鐨勪竴浜涜緝涓哄浐瀹氱殑鏁版嵁杩涜缁存姢                  | 鏀寔  | 鏀寔               |
-| 鍙傛暟绠$悊   | 瀵圭郴缁熷姩鎬侀厤缃父鐢ㄥ弬鏁�                             | 鏀寔  | 鏀寔               |
-| 閫氱煡鍏憡   | 绯荤粺閫氱煡鍏憡淇℃伅鍙戝竷缁存姢                            | 鏀寔  | 鏀寔               |
-| 鎿嶄綔鏃ュ織   | 绯荤粺姝e父鎿嶄綔鏃ュ織璁板綍鍜屾煡璇� 绯荤粺寮傚父淇℃伅鏃ュ織璁板綍鍜屾煡璇�             | 鏀寔  | 鏀寔               |
-| 鐧诲綍鏃ュ織   | 绯荤粺鐧诲綍鏃ュ織璁板綍鏌ヨ鍖呭惈鐧诲綍寮傚父                        | 鏀寔  | 鏀寔               |
-| 鏂囦欢绠$悊   | 绯荤粺鏂囦欢灞曠ず銆佷笂浼犮�佷笅杞姐�佸垹闄ょ瓑绠$悊                      | 鏀寔  | 鏃�                |
-| 鏂囦欢閰嶇疆绠$悊 | 绯荤粺鏂囦欢涓婁紶銆佷笅杞芥墍闇�瑕佺殑閰嶇疆淇℃伅鍔ㄦ�佹坊鍔犮�佷慨鏀广�佸垹闄ょ瓑绠$悊          | 鏀寔  | 鏃�                |
-| 鍦ㄧ嚎鐢ㄦ埛绠$悊 | 宸茬櫥褰曠郴缁熺殑鍦ㄧ嚎鐢ㄦ埛淇℃伅鐩戞帶涓庡己鍒惰涪鍑烘搷浣�                   | 鏀寔  | 鏀寔               |
-| 瀹氭椂浠诲姟   | 杩愯鎶ヨ〃銆佷换鍔$鐞�(娣诲姞銆佷慨鏀广�佸垹闄�)銆佹棩蹇楃鐞嗐�佹墽琛屽櫒绠$悊绛�         | 鏀寔  | 浠呮敮鎸佷换鍔′笌鏃ュ織绠$悊       |
-| 浠g爜鐢熸垚   | 澶氭暟鎹簮鍓嶅悗绔唬鐮佺殑鐢熸垚锛坖ava銆乭tml銆亁ml銆乻ql锛夋敮鎸丆RUD涓嬭浇 | 鏀寔  | 浠呮敮鎸佸崟鏁版嵁婧�          |
-| 绯荤粺鎺ュ彛   | 鏍规嵁涓氬姟浠g爜鑷姩鐢熸垚鐩稿叧鐨刟pi鎺ュ彛鏂囨。                    | 鏀寔  | 鏀寔               |
-| 鏈嶅姟鐩戞帶   | 鐩戣闆嗙兢绯荤粺CPU銆佸唴瀛樸�佺鐩樸�佸爢鏍堛�佸湪绾挎棩蹇椼�丼pring鐩稿叧閰嶇疆绛�     | 鏀寔  | 浠呮敮鎸佸崟鏈篊PU銆佸唴瀛樸�佺鐩樼洃鎺� |
-| 缂撳瓨鐩戞帶   | 瀵圭郴缁熺殑缂撳瓨淇℃伅鏌ヨ锛屽懡浠ょ粺璁$瓑銆�                       | 鏀寔  | 鏀寔               |
-| 鍦ㄧ嚎鏋勫缓鍣�  | 鎷栧姩琛ㄥ崟鍏冪礌鐢熸垚鐩稿簲鐨凥TML浠g爜銆�                      | 鏀寔  | 鏀寔               |
-| 浣跨敤妗堜緥   | 绯荤粺鐨勪竴浜涘姛鑳芥渚�                               | 鏀寔  | 涓嶆敮鎸�              |
+| 涓氬姟         | 鍔熻兘璇存槑                                                      | 鏈鏋� | RuoYi                         |
+| ------------ | ------------------------------------------------------------- | ------ | ----------------------------- |
+| 绉熸埛绠$悊     | 绯荤粺鍐呯鎴风殑绠$悊 濡�:绉熸埛濂楅銆佽繃鏈熸椂闂淬�佺敤鎴锋暟閲忋�佷紒涓氫俊鎭瓑  | 鏀寔   | 鏃�                            |
+| 绉熸埛濂楅绠$悊 | 绯荤粺鍐呯鎴锋墍鑳戒娇鐢ㄧ殑濂楅绠$悊 濡�:濂楅鍐呮墍鍖呭惈鐨勮彍鍗曠瓑          | 鏀寔   | 鏃�                            |
+| 鐢ㄦ埛绠$悊     | 鐢ㄦ埛鐨勭鐞嗛厤缃� 濡�:鏂板鐢ㄦ埛銆佸垎閰嶇敤鎴锋墍灞為儴闂ㄣ�佽鑹层�佸矖浣嶇瓑    | 鏀寔   | 鏀寔                          |
+| 閮ㄩ棬绠$悊     | 閰嶇疆绯荤粺缁勭粐鏈烘瀯锛堝叕鍙搞�侀儴闂ㄣ�佸皬缁勶級 鏍戠粨鏋勫睍鐜版敮鎸佹暟鎹潈闄�   | 鏀寔   | 鏀寔                          |
+| 宀椾綅绠$悊     | 閰嶇疆绯荤粺鐢ㄦ埛鎵�灞炴媴浠昏亴鍔�                                      | 鏀寔   | 鏀寔                          |
+| 鑿滃崟绠$悊     | 閰嶇疆绯荤粺鑿滃崟銆佹搷浣滄潈闄愩�佹寜閽潈闄愭爣璇嗙瓑                        | 鏀寔   | 鏀寔                          |
+| 瑙掕壊绠$悊     | 瑙掕壊鑿滃崟鏉冮檺鍒嗛厤銆佽缃鑹叉寜鏈烘瀯杩涜鏁版嵁鑼冨洿鏉冮檺鍒掑垎          | 鏀寔   | 鏀寔                          |
+| 瀛楀吀绠$悊     | 瀵圭郴缁熶腑缁忓父浣跨敤鐨勪竴浜涜緝涓哄浐瀹氱殑鏁版嵁杩涜缁存姢                  | 鏀寔   | 鏀寔                          |
+| 鍙傛暟绠$悊     | 瀵圭郴缁熷姩鎬侀厤缃父鐢ㄥ弬鏁�                                        | 鏀寔   | 鏀寔                          |
+| 閫氱煡鍏憡     | 绯荤粺閫氱煡鍏憡淇℃伅鍙戝竷缁存姢                                      | 鏀寔   | 鏀寔                          |
+| 鎿嶄綔鏃ュ織     | 绯荤粺姝e父鎿嶄綔鏃ュ織璁板綍鍜屾煡璇� 绯荤粺寮傚父淇℃伅鏃ュ織璁板綍鍜屾煡璇�         | 鏀寔   | 鏀寔                          |
+| 鐧诲綍鏃ュ織     | 绯荤粺鐧诲綍鏃ュ織璁板綍鏌ヨ鍖呭惈鐧诲綍寮傚父                              | 鏀寔   | 鏀寔                          |
+| 鏂囦欢绠$悊     | 绯荤粺鏂囦欢灞曠ず銆佷笂浼犮�佷笅杞姐�佸垹闄ょ瓑绠$悊                          | 鏀寔   | 鏃�                            |
+| 鏂囦欢閰嶇疆绠$悊 | 绯荤粺鏂囦欢涓婁紶銆佷笅杞芥墍闇�瑕佺殑閰嶇疆淇℃伅鍔ㄦ�佹坊鍔犮�佷慨鏀广�佸垹闄ょ瓑绠$悊  | 鏀寔   | 鏃�                            |
+| 鍦ㄧ嚎鐢ㄦ埛绠$悊 | 宸茬櫥褰曠郴缁熺殑鍦ㄧ嚎鐢ㄦ埛淇℃伅鐩戞帶涓庡己鍒惰涪鍑烘搷浣�                    | 鏀寔   | 鏀寔                          |
+| 瀹氭椂浠诲姟     | 杩愯鎶ヨ〃銆佷换鍔$鐞�(娣诲姞銆佷慨鏀广�佸垹闄�)銆佹棩蹇楃鐞嗐�佹墽琛屽櫒绠$悊绛�  | 鏀寔   | 浠呮敮鎸佷换鍔′笌鏃ュ織绠$悊          |
+| 浠g爜鐢熸垚     | 澶氭暟鎹簮鍓嶅悗绔唬鐮佺殑鐢熸垚锛坖ava銆乭tml銆亁ml銆乻ql锛夋敮鎸丆RUD涓嬭浇  | 鏀寔   | 浠呮敮鎸佸崟鏁版嵁婧�                |
+| 绯荤粺鎺ュ彛     | 鏍规嵁涓氬姟浠g爜鑷姩鐢熸垚鐩稿叧鐨刟pi鎺ュ彛鏂囨。                         | 鏀寔   | 鏀寔                          |
+| 鏈嶅姟鐩戞帶     | 鐩戣闆嗙兢绯荤粺CPU銆佸唴瀛樸�佺鐩樸�佸爢鏍堛�佸湪绾挎棩蹇椼�丼pring鐩稿叧閰嶇疆绛� | 鏀寔   | 浠呮敮鎸佸崟鏈篊PU銆佸唴瀛樸�佺鐩樼洃鎺� |
+| 缂撳瓨鐩戞帶     | 瀵圭郴缁熺殑缂撳瓨淇℃伅鏌ヨ锛屽懡浠ょ粺璁$瓑銆�                            | 鏀寔   | 鏀寔                          |
+| 鍦ㄧ嚎鏋勫缓鍣�   | 鎷栧姩琛ㄥ崟鍏冪礌鐢熸垚鐩稿簲鐨凥TML浠g爜銆�                              | 鏀寔   | 鏀寔                          |
+| 浣跨敤妗堜緥     | 绯荤粺鐨勪竴浜涘姛鑳芥渚�                                            | 鏀寔   | 涓嶆敮鎸�                        |
 
 ## 婕旂ず鍥句緥
 
-|                                                                                            |                                                                                            |
-|--------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------|
-| ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680077524361362822/270bb429_1766278.png "灞忓箷鎴浘") | ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680077619939771291/989bf9b6_1766278.png "灞忓箷鎴浘") |
-| ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680077681751513929/1c27c5bd_1766278.png "灞忓箷鎴浘") | ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680077721559267315/74d63e23_1766278.png "灞忓箷鎴浘") |
-| ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680077765638904515/1b75d4a6_1766278.png "灞忓箷鎴浘") | ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078026375951297/eded7a4b_1766278.png "灞忓箷鎴浘") |
-| ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078237104531207/0eb1b6a7_1766278.png "灞忓箷鎴浘") | ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078254306078709/5931e22f_1766278.png "灞忓箷鎴浘") |
-| ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078287971528493/0b9af60a_1766278.png "灞忓箷鎴浘") | ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078308138770249/8d3b6696_1766278.png "灞忓箷鎴浘") |
-| ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078352553634393/db5ef880_1766278.png "灞忓箷鎴浘") | ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078378238393374/601e4357_1766278.png "灞忓箷鎴浘") |
-| ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078414983206024/2aae27c1_1766278.png "灞忓箷鎴浘") | ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078446738419874/ecce7d59_1766278.png "灞忓箷鎴浘") |
-| ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078475971341775/149e8634_1766278.png "灞忓箷鎴浘") | ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078491666717143/3fadece7_1766278.png "灞忓箷鎴浘") |
-| ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078558863188826/fb8ced2a_1766278.png "灞忓箷鎴浘") | ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078574561685461/ae68a0b2_1766278.png "灞忓箷鎴浘") |
-| ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078594932772013/9d8bfec6_1766278.png "灞忓箷鎴浘") | ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078626493093532/fcfe4ff6_1766278.png "灞忓箷鎴浘") |
-| ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078643608812515/0295bd4f_1766278.png "灞忓箷鎴浘") | ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078685196286463/d7612c81_1766278.png "灞忓箷鎴浘") |
-| ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078703877318597/56fce0bc_1766278.png "灞忓箷鎴浘") | ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078716586545643/b6dbd68f_1766278.png "灞忓箷鎴浘") |
-| ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078734103217688/eb1e6aa6_1766278.png "灞忓箷鎴浘") | ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078759131415480/73c525d8_1766278.png "灞忓箷鎴浘") |
-| ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078779416197879/75e3ed02_1766278.png "灞忓箷鎴浘") | ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078802329118061/77e10915_1766278.png "灞忓箷鎴浘") |
-| ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078893627848351/34a1c342_1766278.png "灞忓箷鎴浘") | ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078928175016986/f126ec4a_1766278.png "灞忓箷鎴浘") |
-| ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078941718318363/b68a0f72_1766278.png "灞忓箷鎴浘") | ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078963175518631/3bb769a1_1766278.png "灞忓箷鎴浘") |
-| ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078982294090567/b31c343d_1766278.png "灞忓箷鎴浘") | ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680079000642440444/77ca82a9_1766278.png "灞忓箷鎴浘") |
-| ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680079020995074177/03b7d52e_1766278.png "灞忓箷鎴浘") | ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680079039367822173/76811806_1766278.png "灞忓箷鎴浘") |
-| ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680079274333484664/4dfdc7c0_1766278.png "灞忓箷鎴浘") | ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680079290467458224/d6715fcf_1766278.png "灞忓箷鎴浘") |
+|                                                                                                      |                                                                                                      |
+| ---------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
+| ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680077524361362822/270bb429_1766278.png '灞忓箷鎴浘') | ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680077619939771291/989bf9b6_1766278.png '灞忓箷鎴浘') |
+| ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680077681751513929/1c27c5bd_1766278.png '灞忓箷鎴浘') | ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680077721559267315/74d63e23_1766278.png '灞忓箷鎴浘') |
+| ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680077765638904515/1b75d4a6_1766278.png '灞忓箷鎴浘') | ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078026375951297/eded7a4b_1766278.png '灞忓箷鎴浘') |
+| ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078237104531207/0eb1b6a7_1766278.png '灞忓箷鎴浘') | ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078254306078709/5931e22f_1766278.png '灞忓箷鎴浘') |
+| ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078287971528493/0b9af60a_1766278.png '灞忓箷鎴浘') | ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078308138770249/8d3b6696_1766278.png '灞忓箷鎴浘') |
+| ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078352553634393/db5ef880_1766278.png '灞忓箷鎴浘') | ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078378238393374/601e4357_1766278.png '灞忓箷鎴浘') |
+| ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078414983206024/2aae27c1_1766278.png '灞忓箷鎴浘') | ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078446738419874/ecce7d59_1766278.png '灞忓箷鎴浘') |
+| ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078475971341775/149e8634_1766278.png '灞忓箷鎴浘') | ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078491666717143/3fadece7_1766278.png '灞忓箷鎴浘') |
+| ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078558863188826/fb8ced2a_1766278.png '灞忓箷鎴浘') | ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078574561685461/ae68a0b2_1766278.png '灞忓箷鎴浘') |
+| ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078594932772013/9d8bfec6_1766278.png '灞忓箷鎴浘') | ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078626493093532/fcfe4ff6_1766278.png '灞忓箷鎴浘') |
+| ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078643608812515/0295bd4f_1766278.png '灞忓箷鎴浘') | ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078685196286463/d7612c81_1766278.png '灞忓箷鎴浘') |
+| ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078703877318597/56fce0bc_1766278.png '灞忓箷鎴浘') | ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078716586545643/b6dbd68f_1766278.png '灞忓箷鎴浘') |
+| ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078734103217688/eb1e6aa6_1766278.png '灞忓箷鎴浘') | ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078759131415480/73c525d8_1766278.png '灞忓箷鎴浘') |
+| ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078779416197879/75e3ed02_1766278.png '灞忓箷鎴浘') | ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078802329118061/77e10915_1766278.png '灞忓箷鎴浘') |
+| ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078893627848351/34a1c342_1766278.png '灞忓箷鎴浘') | ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078928175016986/f126ec4a_1766278.png '灞忓箷鎴浘') |
+| ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078941718318363/b68a0f72_1766278.png '灞忓箷鎴浘') | ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078963175518631/3bb769a1_1766278.png '灞忓箷鎴浘') |
+| ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680078982294090567/b31c343d_1766278.png '灞忓箷鎴浘') | ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680079000642440444/77ca82a9_1766278.png '灞忓箷鎴浘') |
+| ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680079020995074177/03b7d52e_1766278.png '灞忓箷鎴浘') | ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680079039367822173/76811806_1766278.png '灞忓箷鎴浘') |
+| ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680079274333484664/4dfdc7c0_1766278.png '灞忓箷鎴浘') | ![杈撳叆鍥剧墖璇存槑](https://foruda.gitee.com/images/1680079290467458224/d6715fcf_1766278.png '灞忓箷鎴浘') |
diff --git a/commitlint.config.js b/commitlint.config.js
deleted file mode 100644
index 3b75ed5..0000000
--- a/commitlint.config.js
+++ /dev/null
@@ -1,22 +0,0 @@
-module.exports = {
-  extends: ['@commitlint/config-conventional'],
-  rules: {
-    'type-enum': [
-      2,
-      'always',
-      [
-        'feat', // 鏂板姛鑳� feature
-        'fix', // 淇 bug
-        'docs', // 鏂囨。娉ㄩ噴
-        'style', // 浠g爜鏍煎紡
-        'refactor', // 閲嶆瀯
-        'perf', // 鎬ц兘浼樺寲
-        'test', // 澧炲姞娴嬭瘯
-        'chore', // 鏋勫缓杩囩▼鎴栬緟鍔╁伐鍏风殑鍙樺姩
-        'revert', // 鍥為��
-        'build' // 鎵撳寘
-      ]
-    ],
-    'subject-case': [0]
-  }
-};
diff --git a/html/ie.html b/html/ie.html
index 87d262d..4d2773d 100644
--- a/html/ie.html
+++ b/html/ie.html
@@ -1,4 +1,4 @@
-<!DOCTYPE html>
+<!doctype html>
 <html lang="zh-CN">
   <head>
     <meta charset="UTF-8" />
@@ -7,24 +7,206 @@
     <meta name="renderer" content="webkit" />
     <base target="_blank" />
     <style type="text/css">
-      html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,canvas,details,embed,figure,figcaption,footer,header,hgroup,menu,nav,output,ruby,section,summary,time,mark,audio,video{border:0;font-size:100%;font:inherit;vertical-align:baseline;margin:0;padding:0}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}body{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:before,blockquote:after,q:before,q:after{content:none}table{border-collapse:collapse;border-spacing:0}
-      a{text-decoration:none;color:#0072c6;}a:hover{text-decoration:none;color:#004d8c;}
-      body{width:960px;margin:0 auto;padding:10px;font-size:14px;line-height:24px;color:#454545;font-family:'Microsoft YaHei UI','Microsoft YaHei',DengXian,SimSun,'Segoe UI',Tahoma,Helvetica,sans-serif;overflow-y:scroll}
-      h1{font-size:40px;line-height:80px;font-weight:100;margin-bottom:10px;}
-      h2{font-size:20px;line-height:25px;font-weight:100;margin:10px 0;}
-      em{color:red}
-      p{margin-bottom:10px;}
-      hr{margin:20px 0;border:0;border-top:1px solid #dadada}
-      span{display:block;font-size:12px;line-height:12px;}
-      .clean{clear:both;}
-      .browser{padding:10px 10px;}
-      .browser li{width:auto;padding:0 80px;margin-top:30px;height:34px;line-height:22px;float:left;list-style:none;background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACIAAADMCAYAAAAWCXEwAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKTWlDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVN3WJP3Fj7f92UPVkLY8LGXbIEAIiOsCMgQWaIQkgBhhBASQMWFiApWFBURnEhVxILVCkidiOKgKLhnQYqIWotVXDjuH9yntX167+3t+9f7vOec5/zOec8PgBESJpHmomoAOVKFPDrYH49PSMTJvYACFUjgBCAQ5svCZwXFAADwA3l4fnSwP/wBr28AAgBw1S4kEsfh/4O6UCZXACCRAOAiEucLAZBSAMguVMgUAMgYALBTs2QKAJQAAGx5fEIiAKoNAOz0ST4FANipk9wXANiiHKkIAI0BAJkoRyQCQLsAYFWBUiwCwMIAoKxAIi4EwK4BgFm2MkcCgL0FAHaOWJAPQGAAgJlCLMwAIDgCAEMeE80DIEwDoDDSv+CpX3CFuEgBAMDLlc2XS9IzFLiV0Bp38vDg4iHiwmyxQmEXKRBmCeQinJebIxNI5wNMzgwAABr50cH+OD+Q5+bk4eZm52zv9MWi/mvwbyI+IfHf/ryMAgQAEE7P79pf5eXWA3DHAbB1v2upWwDaVgBo3/ldM9sJoFoK0Hr5i3k4/EAenqFQyDwdHAoLC+0lYqG9MOOLPv8z4W/gi372/EAe/tt68ABxmkCZrcCjg/1xYW52rlKO58sEQjFu9+cj/seFf/2OKdHiNLFcLBWK8ViJuFAiTcd5uVKRRCHJleIS6X8y8R+W/QmTdw0ArIZPwE62B7XLbMB+7gECiw5Y0nYAQH7zLYwaC5EAEGc0Mnn3AACTv/mPQCsBAM2XpOMAALzoGFyolBdMxggAAESggSqwQQcMwRSswA6cwR28wBcCYQZEQAwkwDwQQgbkgBwKoRiWQRlUwDrYBLWwAxqgEZrhELTBMTgN5+ASXIHrcBcGYBiewhi8hgkEQcgIE2EhOogRYo7YIs4IF5mOBCJhSDSSgKQg6YgUUSLFyHKkAqlCapFdSCPyLXIUOY1cQPqQ28ggMor8irxHMZSBslED1AJ1QLmoHxqKxqBz0XQ0D12AlqJr0Rq0Hj2AtqKn0UvodXQAfYqOY4DRMQ5mjNlhXIyHRWCJWBomxxZj5Vg1Vo81Yx1YN3YVG8CeYe8IJAKLgBPsCF6EEMJsgpCQR1hMWEOoJewjtBK6CFcJg4Qxwicik6hPtCV6EvnEeGI6sZBYRqwm7iEeIZ4lXicOE1+TSCQOyZLkTgohJZAySQtJa0jbSC2kU6Q+0hBpnEwm65Btyd7kCLKArCCXkbeQD5BPkvvJw+S3FDrFiOJMCaIkUqSUEko1ZT/lBKWfMkKZoKpRzame1AiqiDqfWkltoHZQL1OHqRM0dZolzZsWQ8ukLaPV0JppZ2n3aC/pdLoJ3YMeRZfQl9Jr6Afp5+mD9HcMDYYNg8dIYigZaxl7GacYtxkvmUymBdOXmchUMNcyG5lnmA+Yb1VYKvYqfBWRyhKVOpVWlX6V56pUVXNVP9V5qgtUq1UPq15WfaZGVbNQ46kJ1Bar1akdVbupNq7OUndSj1DPUV+jvl/9gvpjDbKGhUaghkijVGO3xhmNIRbGMmXxWELWclYD6yxrmE1iW7L57Ex2Bfsbdi97TFNDc6pmrGaRZp3mcc0BDsax4PA52ZxKziHODc57LQMtPy2x1mqtZq1+rTfaetq+2mLtcu0W7eva73VwnUCdLJ31Om0693UJuja6UbqFutt1z+o+02PreekJ9cr1Dund0Uf1bfSj9Rfq79bv0R83MDQINpAZbDE4Y/DMkGPoa5hpuNHwhOGoEctoupHEaKPRSaMnuCbuh2fjNXgXPmasbxxirDTeZdxrPGFiaTLbpMSkxeS+Kc2Ua5pmutG003TMzMgs3KzYrMnsjjnVnGueYb7ZvNv8jYWlRZzFSos2i8eW2pZ8ywWWTZb3rJhWPlZ5VvVW16xJ1lzrLOtt1ldsUBtXmwybOpvLtqitm63Edptt3xTiFI8p0in1U27aMez87ArsmuwG7Tn2YfYl9m32zx3MHBId1jt0O3xydHXMdmxwvOuk4TTDqcSpw+lXZxtnoXOd8zUXpkuQyxKXdpcXU22niqdun3rLleUa7rrStdP1o5u7m9yt2W3U3cw9xX2r+00umxvJXcM970H08PdY4nHM452nm6fC85DnL152Xlle+70eT7OcJp7WMG3I28Rb4L3Le2A6Pj1l+s7pAz7GPgKfep+Hvqa+It89viN+1n6Zfgf8nvs7+sv9j/i/4XnyFvFOBWABwQHlAb2BGoGzA2sDHwSZBKUHNQWNBbsGLww+FUIMCQ1ZH3KTb8AX8hv5YzPcZyya0RXKCJ0VWhv6MMwmTB7WEY6GzwjfEH5vpvlM6cy2CIjgR2yIuB9pGZkX+X0UKSoyqi7qUbRTdHF09yzWrORZ+2e9jvGPqYy5O9tqtnJ2Z6xqbFJsY+ybuIC4qriBeIf4RfGXEnQTJAntieTE2MQ9ieNzAudsmjOc5JpUlnRjruXcorkX5unOy553PFk1WZB8OIWYEpeyP+WDIEJQLxhP5aduTR0T8oSbhU9FvqKNolGxt7hKPJLmnVaV9jjdO31D+miGT0Z1xjMJT1IreZEZkrkj801WRNberM/ZcdktOZSclJyjUg1plrQr1zC3KLdPZisrkw3keeZtyhuTh8r35CP5c/PbFWyFTNGjtFKuUA4WTC+oK3hbGFt4uEi9SFrUM99m/ur5IwuCFny9kLBQuLCz2Lh4WfHgIr9FuxYji1MXdy4xXVK6ZHhp8NJ9y2jLspb9UOJYUlXyannc8o5Sg9KlpUMrglc0lamUycturvRauWMVYZVkVe9ql9VbVn8qF5VfrHCsqK74sEa45uJXTl/VfPV5bdra3kq3yu3rSOuk626s91m/r0q9akHV0IbwDa0b8Y3lG19tSt50oXpq9Y7NtM3KzQM1YTXtW8y2rNvyoTaj9nqdf13LVv2tq7e+2Sba1r/dd3vzDoMdFTve75TsvLUreFdrvUV99W7S7oLdjxpiG7q/5n7duEd3T8Wej3ulewf2Re/ranRvbNyvv7+yCW1SNo0eSDpw5ZuAb9qb7Zp3tXBaKg7CQeXBJ9+mfHvjUOihzsPcw83fmX+39QjrSHkr0jq/dawto22gPaG97+iMo50dXh1Hvrf/fu8x42N1xzWPV56gnSg98fnkgpPjp2Snnp1OPz3Umdx590z8mWtdUV29Z0PPnj8XdO5Mt1/3yfPe549d8Lxw9CL3Ytslt0utPa49R35w/eFIr1tv62X3y+1XPK509E3rO9Hv03/6asDVc9f41y5dn3m978bsG7duJt0cuCW69fh29u0XdwruTNxdeo94r/y+2v3qB/oP6n+0/rFlwG3g+GDAYM/DWQ/vDgmHnv6U/9OH4dJHzEfVI0YjjY+dHx8bDRq98mTOk+GnsqcTz8p+Vv9563Or59/94vtLz1j82PAL+YvPv655qfNy76uprzrHI8cfvM55PfGm/K3O233vuO+638e9H5ko/ED+UPPR+mPHp9BP9z7nfP78L/eE8/sl0p8zAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAC7ESURBVHja5Lx5dFRV1rBfgHwYRQQVtB26ZWhtabtfeUGxGxFbUGZF8RMHGkVbRkekVYiKisicVhE0gEwBokgDAhEMMSSQkAECwcxkrlRSqVTqJqnxzs/vj5t7qUyAvr9e37fWV2vtleSm6p6n9t5nn733OVU2RaUaEP5PiqJSbeMXPBTA5/Xhzk9Vnd9vo3HFx21E2LYJX9IRgh6npvyCe9uaqS4K4C3IpXHFx9S99CTuJ8Z0KLVjRlA7ZgTuJ8ZgXxmJL+kIlwAkXBQk6HFq9pWRVA8fSvXwodYgdS892a6EA1UNvouqwXdR99KTeAtyfz2IL+kI1cOHYh9wqwVwKWJqpXbMCOv19gG3Imzb1JF2OgZxfr/NukH4jcNVfyEAE8IU+4BbKet1PfaVke3BtA/i/H6b8aIBt7a4mWmaC0nr55vmqRp8F5V33Mm5LhHtwbQF8SUdsSDCb1I1+K42g1xIWgOYYh9wK+e6RCBs29QxSIWus37aJM51iWjx4so77mwD1d5AHQ1eecedlN9yuyVlva6nrNf14Q7cEmRn4W7u3T2E9ME3UX7L7W1uZg5Weced1s3sA2613ql5LXzQjuRclwjcT4wxTXQeRHC7GLdnHPeensiCVwa3e0PznZk3EbZtwluQa0kofz8NcVNxr++Ce30XnNuv61Bcu7viXt8Fvyu7JYipjfGHxzD+8Bh2j+7fAiZcC+Y0zPDIbCyD6DyV6DyVeDcIQR2C39J4oieNJ3oSOnkVcnZ35Ozu6MVdDHF0N6S4C43OqJYg/0ydzb27hzDx0FjuPT2R+asfa6OVsl7X40s6QoWus/CQk6fWZPHChhxe3lbMCxtyrN9TyxSQSwidvMoC0XK6tRGybPjSRmOuNUKVo4Zxe8YxIu4+Jh4ay/jDY7j39MQWWjnXJYLGFR9Toes8tSaLiavTrIHDxfxfapkCwW8hy9YuhCmhk1fR1FRnaCS1NM4yy8RDYy2tjIkZRXq/HtYsCnqc2sJDTkYsTrU00J6YkEJQR7M/eEGY0MmrcOenqjZA2JmyzTJLuJiOe65LBHUvPUmGR2bE4lQmrk7jqTVZHcrE1WkMWpRIdJ4KnpUXBCHLRl3e16EWIOEaMU00/vAY9na/gsYVH/NdgYe+8w9bMBeSQYsSWXjICcFvL2ga+dhlFwcJ10rjio/ZklprgbSWiavTWvzdd/5hXt5W/OtATC201sq9u4eQ+PVijmSW0nf+YQYtSmTQosR2gUYsTmXQokT6zj9saeRCpmkJ0hxD2gOZeGgsI+Lu45+ps7FXlFmDmDDtSd/5h+k7/zCpZQpa9cwOQciyIR+77LyzFhXlMyZmFOP2jLP8orVWRsTdR2ppHFtSa+k6ZZM1WHvSdcomwyxySceayO4OWTY88TdirygzUkWf18eL2//RQiutYcwYE/Q4tagDOUQ8uo6uUzbRZ3qMJV2nbCLi0XU8tSbrolNXzu6OfOyylgEN4NOkaO5acw/j9ozr0ET37h5imehIZimPL91rAfSZHsOQBfuISS7E7vaTETeX0MmrOoQInbwK+dhlNKWsahni0zPSuGvNPW1M1BrI1NrOwt0WkCn2ijJSS+MYt2ccuQk3oxd36RCi8URPY+HLT1VbgGiSzPsx71laCddMe2Yygf6ZOtuScXvG0XfJn/n8YL+LQnjibyQ34WZ8Xl/bfKSoKL+FVi4EYwKZcu/uIQzaPoExMaPQcrq1ADFX33AI1+6u1OV9HVI6ShU/TYqm75I/dwjTHtDEQ2MZt2ccg7ZPaGGScIDWEBlxc42UoSMQ00StYdoDCgcbtH0Cbx+8p40ZTIBwiFM7RmB3+y+exZvT2YRpDdR6ZoVrw1xRWwN44m/Euf06A6Ki7NLrmnDNmH7TEdSg7RP4/GA/yLK1GdwEKNzSk1M7RlDlqPl1JefOlG2MXTGmXaAxMaMsB/XE34h4tH+7ANlrB7T2iV8OAlDlqOH9mPcsIBPKlF3R16Ad7GwlxoVberYAKCrKv1ghfmkg5sPldLIzZVsLqLErxpC9doAlp3aMICNurlGyVpRdSAu/HqS1Q58rd1JUlI87P1UtKsrHXlGG3e1HCOoov+x2wiX3RxT+o49L1IgutXxVUCfDIxNfLraQDI+M3e3/NdCXbhohqBNfLrIsVzZqmoT6dmXG0SBLTrmJLxd/CVRLECXcDGFaSC1TmHE0yKg4B0P2uxiy38WoOAePHaptAfHYoVqG7HcxcGc5o+IcfFfgsbQUPoYoSa213BbE78oGucTSwpJTbobFFjNgbQHdvi6g8/Z6Om+vZ8h+VxsQE7T/97UMWFvA+Og0UvIryfDIZBQ4CeXvt8a5IAhAY/RImlJWUaHrPHaolhuXFXHN+8e58qNcbomq5P6t3xG973WePLzPgnnsUG0LiP7f1zJwZzk3LisyctfSOFxOJ4lfLzYToQubxu/KpmpWBFWzInguOokrP8ql7/zDRMxLpFfUabasHwlZNnITbmbgznI6b6+3Bu7/fa2lrW5fF9Ar6jQD1hYwLLaYx5fupdi+EiGok748koa4qa010xKkKWUV2UM7kd6vB7tH9yfpnUFkLzQiZOGWnmgHO9N4oie9ok5bA4YPbkqvqNNc8/5xIuYl8tSaLOLLRXambENXF+PxNJD0ziAanVHhYaEliH1lJD/1iqD0qSsIzu2M/N550TZ3QjvYmS3rR1qDtwdhgpgwnabGMj46zRjQsxJdXYw7P1X1pY0GuaRjkMKxPah5qxuV8y6nct7l1LzVDfdyo6miHexM+ou9mblwKfdv/Y77t37HNe8fbwMQDhIxL5FOU2PZklqLJjUYdU7wWxBuN+ricBAF0KQG6pcNovZpw0fCQao/MEBcu7tSOLYHjnu7EZzbmeDczqyfNokrP8ptMXi4XDnzAJ0n72TIgn1oUoMB4VlpgIjj24I0payi9KkrqHj+Ssth2wM5c38f8p68D2nbHKRtc3h86d42A/eZHsOVMw9Y0nXKJmxDvyS1NA70z8Gz0qh5hNvbzpr6ZYMofzyiBUwLkOVdjfR/eVcao0dSl/d1aHx0GhHzEi0TXDnzAJ2mxtJpaixdp2yypM/0GLrcs5D3Y94ztNDsK7qjuxmzDBBz2rYGqZoVQc1b3dr4yfppk+g8eWeLd91aAxGPrqPbyKV0G7mUiEfXMWdz+nmQ0Jsgn1AbT/SkMXrkeZC6vK9DpU9d0S5I5bzLqf6gq6UV7WBn5q9+zDJBuEQ8us4SE6LLPQvpcs9CjmSW4ndlo1XPNBxWLiE34WbSX+wNapEBEsrfT/njERSO7WGBmDA1b3Wj9KkrSO/Xg1WjBjJl/CT+8sQ8a0BT/eGDhwN0uWchXe94ia07YkE+oSLc3gxyQt2yfiSrRg0E+YRqgRSO7UHh2B4UT7ragqmcdznFk67mp14ROO7txpTxk7AN/bLFgN1GLsU29EvrejiACdG59xQjKgu3GzVP9UwIvcmCVwYb102NmBHVBDFNVDUrgjP39yF98E0E5xox5Dcj5lsDhwOYQObg4dK59xR2RV8D4njEo/0NIEd3dkVfgy9t9HkfMTWSO6pXG63kjupF8aSrqXj+SoJzO1M573KmjJ/Eb0bM5y9PzGPBK4Mp3GKUEFvWj+Q3I+a3AOjcewp/eWKesUQ0T1mz2att7oSU9+F5EE2SqXvpSbKHdrIGNmHCoapmRVgh33LezZ3QNncyloGDnVnwyuA2IFvWj0Q+dplREzu6Wy0r9/KubVvg9pWRpPfrwZn7+1haMSHCxdSM/J4RWWufjiC9Xw/m9PgtN9w0uo1JbrhpNI0njAXTrAIbT/TEvb4LjdEj2641vqQjpPfrQfrgm1qYKHxKlz51BbmjerFj4G2WtAYwtWDKglcGG2ZoXrldu43AWDUrAmnbnLaRVZMayHvyPn7qZThoa38pfeoKap+OIDi3M6tGDeSGm0a3GTT82g03jeaGm0bj3H4d8rHLrN0I93LDpDsG3kb68si2a425hfZTrwjSB9/UBiZcM+YM6ghoyvhJpL/Ym+yFhknc67tYQVF+z3gjc3r8Fuf32zpOFTMeHXpRGDNfMYF2j+7PqlEDWTVqIOkv9rZ8SNvcCff6LlTOu9yK1Okv9mZOj9+S8ehQNKmBDhs17vxU9adeES1gwoHKH49oFyhcwhfKynmXWzOu4vkryR7aieyhnQjl7+84QzNNJGzbxN7uV1gw7WmntYZaLw2mmNdrn44ge2gnztzfx9od7zBnDa9t0pdHtgsTDhRustaaCndwEyLj0aG481PVS9r3FSUJj6eBrConMZHvnodpntrh2gkHCgcLl/TBN7G3+xXGLMlIo0LXjU7ixeoaUZIQ3C7OlTtJya8kJvJddgy8DctvWgGFaylcHPd2Y2/3K5jT47esGjWQrTtiyapy4nI6jUrvUmpfUytFRfmkZ6SxdUcs66dNYsfA2ywNtQBrJeb/dgy8jZjId/kx4YgF4fP6Ln1L3uyhhWvnSGYpOw6lEBP5LuunTWLDAw+x4YGHrAi74YGHWD9tEuunTSIm8l227ohtAyBK0i8/pNDagTVJxuf1YXf7OVfuJKvKMF16RhrpGWkcySwlJb+SrCqn1awRgjqaJP9nO0b/Zxo1v+ahS0ZqKJ9QCX5rJMyhN42aRj6h/udB5BKjiAp+i64uNrJ2M0Vs3rUiy4aU92G42X49iCYZDZjUMoX4ctFIcILfGgVU6E0LwEyCxKP98aWNxpc2GvFof+RjlyHlfdjxWnOxh93tJya5kIWHnDx2qJbnopP4NCmaYvtKC0LL6WYkQps70RA3laaUVbjzU1V7RRn2ijK8BbkWUJsM7VIAog7k8MyuPKtD1AJA/9zQQpYN9/oubFk/kpkLl7J4a0KbtrdZa/vSRrfMWS8GcSSzlGd25TH5VIjptTpR9T5SS+OMsrHZD3RHd7SDnTm1YwSzY2KsTtL46DSei07iSGZpm/tKeR8a5gnf0+vI8zfE5zAstpjptTrvifBJeeZ5LTQDkGXDtbsr0fte59mjDmaWaUyv1ZlZpvH3XJlRcQ6Grj5OTHJhy/t7VhrpwMVAog7kMCrOwcs+nZWaccak2L7S0oLpC6d2jGDJiUyWN8E6FVZqsLwJ5ruwYO5O9jFoUSIb4nPOT+/gtxf3kZjkQobFFreAaHRGGZoQbm+hhWd25fHsUQevHilgbo7bAmoNM2S/i6Grj3Mks9Tolcgn1Hb39MzHuXInw9edZrJd4z3xPISuLrYgCrf0ZOuOWKLzVFLLFDIKmlfr5EJmHMxhfoWvDczkUyELxl5RduFUUZNkIvdm8+BpkZd9eocQPyYc6XDnocpRQ+TebObmuFmptdTK5FMhBqwt4K1vMi4cWTMKnIyKczDZrvFJeWaHEBdrbVc5aphxMIflTR1rJaPA2TFI1IEc7k72tZwdYRCLtyZc6h4MMcmF7WrlwRSRAWsLiNyb3T6Iz+vjmV15jIpztIHwxN/I7JgY4svFS47CHk9DG62Y5hm4s5zx0Wntb0CnlikMiy3m06ToFpFSO9iZnSnbeGZXHkcyS8kocF6SHMksZc7m9AuaJyW/si3IltRaZsfEGNM09KZVs2bEzWV5EyzLlXn1SEG7MuNgTruy5JS73dlzd7IvPMi1BIlJLmRnyjbLJFawar7ZHi5NdrSS9jRyd7KPXlGnzQDXyjSlcYY2mk1SuKUnS05kslI7f9M9/HKgdaoh74nn/cR02NV7M9t2A9A/t/qf2uZOvB/zHvNdxk3Mm0bV+36VzK8wxHTWVutPmEbkE6q1hjQ3/yefCvGeeB7k1SPGlLsUeeubDOtnezJnczpvfZPBuXJnGEjzAqSri9FyulG4pSf3b/3OCvErNQNmxsEczpU70ST5kuWXJc9yiZXemQ3du5N9TK/VedmnW1qZm+M+v3r+gpTS42nA42nA5XRa4vE0hFd8zSDBb63cInvtAAYtSuTuZB+T7ZoFYy7tz+zK6+igQZtHRoGTyL3ZLab4M7vyGB+dxpAF+1i8NaEliLmWyNndsa+MZPi60/T/vpaJhTKT7ZqllZWaoZW3vsnA42m4IMS5cifPRScxN8fNeyK87NOZXqszsdDITa55/3i4dgVb0OPUTG2IR/vjzk9Vt6Qau5R3J/uYWCi3MJEJM2dzOkcyS80Q3WKrPia50IIIX2cmnwrxYIpIr6jTPBed1Mo0apFgpv0NcVMR3C5ESWLO5nS6fV3Ag6fFdmHmV/iYcTCHyL3ZRB3IsSRybzbP7MpjfoWvXYj+39cyZME+c7aEgTQ36smy0RA31dostrv9DF193IIJ9xcTxgSam+O2xAQwg9fMMo2JhTIPnjYgBi1KbC+RPq8REyR8iT9X7rRgWptpvssYLBwqHGB6rc7fc2ULYsh+F4MWJbLjUErH09c8ytcaxNTMCxtyGLC2oIUDT6/VO5TJdkMLJsTAneUMWpTYNotvE0eaj3rKxy6zun2t69mdKdt4fOley4lN35ls11pIOIC51D8XnWQu9xcGUQCteibyscuM5n31TKNqD5fm1H9DfA7PRScxdPVxhsUWMyy22Dq4MGS/i2GxxQxfd9oC2HEopb1WVcdtCU2Sqcv7OmTWpGbRLOV9SCh/P0GPUwvPvDIKnMQkFxK5N5s5m9N5LjqJ56KTeOubDFbvzSQlv7LN1P5FxzZ8Xp918v8SWk5WsWStLbr0a5oLHRdY/+GjPP8vtq7+0yCiJOHz+hDcLlxOJ2bzxeV0Irhdlk/9x0B8Xh9VjhoEt6s5rZTaFU1qQHC7qHLU/PpZ05EGqhw1uJxO0CVESSIlv5KoAznM2ZxufTJgzuZ0og7kkJJfaR1mcjmdVDlqflkc6ahSs1eUWdMzJrmQQYsSrYMJNy4raiHmYQWzD2IC2SvKLpa/dAzi8/qsc6cZBU6GLNjHlTMPcEtUJVMSdd45qRGdp7KxDOvDPu+c1JhxNMgtUZVcOfMAQxbss0K7vaLsQqbq+GCtCbEhPodOU2O58qNcZhwNsrMK4t0Xlp1VMONokCs/yqXT1FgrE7sATPvbJK0hblxWxDsnNWugvc7zcqFry3JlbomqbANzSdskpk9kFDjpOmWTpQnzne6sMgbbWWWYY8kpN0tOuYnOU1v8z9TcOyc1blxWRNcpmwwz6dLFjxr7vD5rY+eO13YSMS+Rh/co1iAby4wBluXKLDnl5rsCD1lVxk7FdwUelpxysyxXbvHcjWUwYb9CxLxE7nhtp7X10spELUHMMiHqQA6dJ+9k8KYaJh1u6ZRLTrnZklrb+hS3lURtSa1lySm39fyNZTAlUWfwpho6T95p1rqtS5LzICapJsmWNkbEBpiSqLMs1/gY3DsntfAuT4tDlkrYtci92bxzUmNjmaG9KYk6I2IDbbTStsBqjhma1EBKfiVdp2xiwNoCHt6jMOmwxjsnNev46KWUkaIksfCQk2W5Mu+c1Jh0WGPCfoUBawvoOmWT1d4Miy3nQczIuXpvJp2mxjJ4Uw0T9hsg09KM6fhcdBIxyYWXJM9FJzHjaJBpaTDpsAEzeFMNnabGGhVec+RtA1LlqAFd4vGley0Q8wZTEnWmpWGdWX3sUC3PHnW0K+b/n0qoZ1oaTEszfCQc5PGle0GXwv0k7PxI87S9EMjMMo35rvMdILPDbErrzlA4iOmw4SBh0/iXgUxLg8mnQvw9V2Zmmdau/D1XtpoxpiYe3qPw8B6FW6IqreOCvwpkWhqMinMwaFEi46PTfrFMXG38HLr6OHe8ttPykXZNYzrr4q0JdJoay4C1BS2cdfCmGuZsTrd6Hv/T5ozZJ7no9L1xWZE1fU0bD193unXx3GESFZNcyIb4nDazaUN8Dh6PkTy1O307CmgT9itM2K9YWnkuOumi26wTV6dZR43NXOXKj3LpPHknEY+us0DaDWiWnwCr92bSdcomBm+q4eE9ShsThTXh2jRn5mxOZ/CmmjYzZkRsgE5TY40Q33bhu/iiF66VcJjh604TuTfbUnnk3myGrzttQZgzZtJhzQrvfabHWGNccNELnz2tfSUcJjxADVhbwIC1BdYsMyOp+fyH9yhWGnAks/TS0gDTV4qK8q2NxU5TY7klqrIFTDhQ6+gZ/hwzdoSbpKgo/9LPj5hnR8yUwEwVw810MRkRG7BSRXPpLyrKv/RUsT2YI5mlLZLnEbEBK1q2lhGxASt5vuO1nZY5ioryL5TJX7icENwuioryjV1rr4+oAzkMWbDvouXEkAX7iDqQg8/rQ5MaLgZxaQWWJslWSWkWWBkFzl9UYP2PvgjFPNrj8/osM/2YcIQfE46QnpFmfL7K7SLocWpBj1Mz6+D0jLQWzzPb3b/6aI8SVnCbvXTTVOZxno6kqCjfKlPNUH4pIP9XPGz/N319UFnrf2iKLGi6LmggqCBoIOi6JuiqIqCrgqIrgqyrgoYu6JpiiK4LKgigCpquCCEdQdVVAU0VdP2iMGW29tplmtbcQNQ1QEXXNDQdQGsWHZBbvdQsKkTQfaiaBJrc/PyLPpQ2zqqbL9U10GV0TUbTZUCyQAoaJPaVinx5RmbVKZnVWRpf56r8WKlQFww2Q4bf8VdMXwsEtfkdGb97xSAb8yRG7df4zYYQ3deEsK2WsK1UsK1U6LIqxJWfKQzcEODVw0GS7KbG1F8Pout6C7WuL5Dpv1PBtlLEFgWXfyHTY61Ery91rvkiwLWfB7h6jcxV/5LoskLF9gl0+tjLI7FesuuxzKnrHeqneQdL143Bjacj6wqg4ZFUph8JYvusCdsXIldvhGvXi/T+SuS6dQrXrZO4fp3Ib76UuH5NiD6fi1z/mcgNnwa5epWMbbHG1StEvsoSjbeoq2i60h6MYNN1XTAhNF1vdlBoVFSG7/Nh+1Ti2o1Brl8v03uDyDVfN3DDVz5u+FKh15cKvdbp9FoHvT5X6PW5wjVr4LrPda6NkugTJdL1EwXbIpkVx5sdGaXZ8S9gGgNIJ6ipPHgghO3TED23h+ixTafXZpmb1ofos0ml+9dw1VcaV3wapMvKIF1WSVz+qULPzxV6faZw9Wc613yq0Xt1iN9Ehei+WMG2QObz03JHDtxsGk07P2XRmZ/hx7ZG5rqtMjdubqTHFonrNov8doPMZRvA9pmPqz8X+MNWhb/tkrg/VuGWaJXLPmmk85Imen6m0+sz6BMlcsNqP9etVujysU63jwIcrwy1N6UFm6Zrgma4KKBxrE7lyq999PnaT58dcMNWjV5bFa7d6sP2lcj/+szP6/FNHK2SqQtpSKqIKItUN2psyJH52yYXtkV+uq9UuP5fMj1XqVy9WuWGFSE6LQgxbHMQv6kVXW92B12wKZouSEjGNNMVJvwgYdugcGOsym+2q/TZqnD9dh3bVz5u3h4guVJtnpJa808zkJlBMMS7SQG6vB/gimUKvVdK9Fmu0nu5zLXLZGzvaWzLDhggmoysqwYIKoKqG+rKqVO5douP62JUfvutxg2xCn1iZTpv0rgpRuF0XQAIgRJElSUURUWWZWRZRpFlgrIKeIEg7yaC7X2FXkslei+XDVkmY1sQ4pFNDaA3hwcdNF0XbGjNZwNQWXZaxrZV5XexMjftFLnpW4ne34rYNvjZUywBQUJqEEkMoEk6oqIgySqipCCKEt6Qis8fRNEaAB+TtijYInV6Lwtx7VKRPstkIj5S6PGBRGFtwFCgApquCDYFTQANXZeZkiARsVPnlu9kfhcr0/cbiYivA4w94DM0oet4VQVJUQiJGiFRIiTKBEMSAX+QhoBIvU/C1SQCfpIKGrl8kZerFitcu0Tkuk9ErlsiYXtDYuMpYyobE0gVbIouC6DiDsgMiwtx406Z/rs0+u6WGPCNSI8tIZbnSoCCEvITkBRkWSMUkAgEJbz+EE2+IA3eAPUNjTR6fNTWSni9PuoFN/d8KtBpkcg1n3jp82GQ3h/6sc33seAHb/P6pYOmCTY0VQCNEkHhrgMhfrdL5k/fafT/XqT/boU+sRI/2r0AhESFYFDCF1TwBSWa/CE8TQHcjQFcDX6cdQGq63w43PWU1AoEmup4emMjtvl+enzop/d7Aa57N4Btvo/Z37jCHBzBpuqaAHDOHWDo/iD99in8+XuZO/er/H6fxsB/h0irDgGqoYGAhOAL0eALUd/oo87TRK2nCUddI3anQKXTTUl1DUVVNXga6nh2mwvb6066L3Bz3btOekU2YXtd5MVNDmuVVtEFm6brAmiUu4OMPODnjv0idx+UGHpQ4q6DEnf928+h0iCg0egN0OgXqW8MUCd4cXm81LgbqHIJlDs9lFd5KK90U1hWQ3GlgLOqlrs/rsQ2q45rFjq57q0yukc6sL3iYc62akBDR0fRNcGmq5oAQYSAyuQEibsPhnjgkMYD8T4ePOTnv/ZrfJrtBTWE4A3ibvRTJ3hx1jdRXddApbOeMoebEruL3Ao3p8vqOVVSR1JuDZkFtbywvgDb0zl0eqmanm+Wct2bFdhmlvP2Po/hH6qIrmiCTdNUAVVElTVeyfTz10My435UGHNE5JGfJIYf1ZiV4kFo8uILBKirD+LwBHC43Dhq6ymurqfAUU9ORS05RSU0NHmQVRW/JCMqOho6354U6DEri04z8+nxWim2fxSx8ZgLEAlJCqoiCzZZUwVZVECDjUVNDD8s8sRRlSmJOs8mwbPHZJ466iO2yI8aDNJU56a8tpGqaicOZx2FVfWcLa8lq7CMBn8IHfAGZQKSik/SQAoBOjEZtdiezqTTS/l0fzmPrFIBNB9CUCcoSYJN0TTBKymgS5TXBXn8pwCTj8lMT1WZmarx+nGR2Rk680+GOFleh9/bgMtZR3V1HYWVLvJKajiTV0pVjRsV8IVEgrLaLApeERSCAAx5/xS2+48zZvlZAmKIQFMTHq+PppAi2DRdFQIyyKIfRImoMz6ePO7lpUyJl08r/PN0iMjTEh9kS6zNEUgp92GvaaDAXstZh4DLG0JoChAMyviDMn5Jxi/K+EISIVnFr0h4JWPZ33a8mNteSCI6vhpZbMDhaqChyYfHHxRsmhYURBECoRDoMvkukVfTFN7IlHk7W+aDXIlVOSHW5ob4qhi2F4v8WNLIiSov5wLgkVVERSUYMqa2LyTjF1UCkkpQ1vGLImJAxCsai2SdKFJQ6aG0ooqK+gBuVxOCTxBsuq4IkqQSFCVCkgyqzg8lXt5J9/H+WViVJ7G+KMSOEoVdJSp77DJxdRrH3Rq5goLDJyMERRqCIt6QbPiHqBAQFSRJJSCrhGSZJklDUs/nIefsNRRXe3DWefE0NjUf21BURFEiGDRWVH9I5Nu8Rt7Pk/lXocbWIpFvKzT2VSr8YJdIcEqk1Svke2TsPhV3SMYTEmkISngDCr6QTFBSCUkqQUnFL2kEJUNLflFF1aGuyUepow6HuxG34DdyVkVRkCQFUVLxBWR0ScEfFPmuuIG1hTIxpSr/rpA46FBIqJHJdGmcqVPJa1Co9MrUBiTcQQlPQKYhoNAUUvCJCn5JJSApBCTZEr8oEVJU/IpKiaOOmnov9Q1+QyOqqiErGqKiIYk6/mAATQ4QalRItPvZU+EnvkrmxxqJRJdIVp1KTr1GQaNChVei2idTE9BwBRTqAzKeoEyjKNMkKvglhaCkNAMZogAeX4DS6npcDQE8jYHmM0aajqLqyLJOSNbwSTJev0woEKCxyU9OdZCEkgAJ1UGSBYWsBo3cRihq0qj0KVT5ZBwBjdqQRn1IRhBVGiWVRlklqOiIikZQ1hAV4ytjJE2n0ummqt6LU/AjNAYEm64jaBqoqo6iaEiKhiirBESVhkAQr9eH0ChSUu3nVGkdGY4mUmt8ZLoC5DWoFDUplHpVKnw6VT6ZWn+IuqCEJ6TQEFINzUgSflXFJ8nUe304XALVdQ3UNwaob/TT5A0ZILoO4TCyrBKSZHxBGcEfxNPgpdETwO32U+ZoIKesnrPlHrLtbn6urCfPXk+B3U2R3cO5qgbOVTVQUilwrkKgtEqguLKe4sp6yhwNlNg9VLkEhKYgjd4QTX6RYFA+X2Dpuo6maaiqiqqqKIqGKKn4QwrekERjIIC70YenMUBjk0S9EKK23our3ovb48Xj8SI0BfD4ROq9IdyNQeoa/Lg8AZxuPzV1PuobRASfguAN0egP4Q1KBEMykqwKNkAxMnpDNM1oSxhQGrKiI6oqTapIkyTiDYUIiDLBkEwoICOGjHghKxqKqqCoEooqEVJFgkqIkBIiKIsEpBB+MYA/FMAXkgiICiHRmK2KoilWo6bZRIKu61bjRdd1QdEQVBVBkzRBlVRBFhVBVTRBUXRBknVB1hAUECQQNF0XUHVB13RB0XRBVDRBUjRBUTVBUlRBlBRBlGQhJGuCJOuCouiCpuqCqqpl/7Eemqor5HnS2Ja/hPezpvCP1PuYlfo3vvo5EnfA0baH9qs+CKZpBIIh7DUuyuw1lNprqHDU4mnwoqoamq5xyn2YVTkv8cKJO3n+TH+eTB7Ao/H9eSr+TnbmrfyfgdiddZzKKaK0yklhuYN6oWVfvabay+6Tu3gzaSJPpPZm9E9XMmnvH1n60wKSanZypuEg35WuZlrCMLb9vPSXgzicdWTkFLX7vya5Dq/spk62s8v1AW+cu53ns29kSd6z/Fi9mZ/L8tpqVFfZeHYxBe7MSwdJy85v8Xd1oJwDFRtZlTeTD88+wcKsMSzMGsv8rL8wNbMnc7LuJN6xg6AcsF6TW1xBkzfQct9P8pDrSkfT1QuDKKrKz8UV1t+V3kKi89/m1YyhvHlyMPOz/ouFZ4fwYe59fJAzjLfO3s66wuep8p7jbF0iUTkzOe76/rzZ6jxUVteGtch06gL2C4PIikJFtcv6e3/ZeuamDOHNU//NivwxfFY8jnXlE/iyYiKflz/Eh4WD2Gv/CL/YQIJjI2+dvJvXTt7FtJS+LPt5OvVBY383KEoUlFaGzSz5wqb5ubC0WSsyG3PfZUbKnXzw8wOsKX6EdWUT+NI+nq8cY1nrGMnikjuJd0Xhld1sr3iTt37+IyuLHmZN0WMszxnPzLSBvJnxMMWNPxv7vUITLrdw8VlzMswnNud+xD+O3cGy3LF8ce5R1pZN4IuKsXzlGM0X1SP4uPJ2jgpraJAcfFb+CJHnbuOz8pF8UT6OL0om8nnRJFblPcrLaXfxxolROHzGd2idq7xIHBEavTQFQwAcLNvMP5Lu5JOcsawpmsRnJROIKnuYtVWjWVP9Vz6q7McRz0pUTSa2Zh6LSgeytOJPfGa/j3UVY1lTMoFPz01kdcEjLM95hNmp/8UHmU+j6MYnlrJyz3UMknHW0IbDW8rLyfexIGs4nxU8zqqi8Xx07gGiKkfyheN+ltnvJEFYGdYOFWlUqjniWcGK8iFElQ1jTek4Pi2awOqCR1iZ9wgfnx3Hs4l9+aHc+BqH2voGRFFqC+JpaEKSjOR2Y84iZqX8majcx1ieN57Xc+/hvXPD+aziAZaX30VGY0yH0/1s00E+KR7KquL7+ezceFbnT2BFzkSW5Uzg7VP38UbKQ3hCdc1aKWoLktHsG06/nbnJ9/H+6VGsyJnIC9l38kreMNaUPsKSkkHsdy26aABMcK3lw4L/5l9FY1mdP56lOeP55Ox4Psh+mOeT7+BAyUZj17O8qiWIKMkUlNoBOFQaw4zkQSw+M5bZp+7in7mPsKnkFVade4DPSsfTJNVeFCSk+lhbPIVl+Q+wMnccS8+OY/GZsXxwZjTTj9/OkqwXACi3O/H5A+dBKhy1lFQac33t2bf5R/KdvJnxFxadnkSyYzuf5j3BssIR/Kt4DBvLp/NF2dOsqXiSNRVPsKbyCeNnxZN8XjaFz4ufJrr4Bf5V8Agr8h5iWc5YPs4ey4enR/P+6YeYnfZn3kh9CAUfqgz2Gtd5kLOFpZTYjUMHH516jmlJA3jjxHCO2XexteBtFpwZyqqC0awo+huLCv7Eu4W38V7x73mvtD/vl/Xl/bJ+vFfye94tuo2F+X/g3dw/szT/b6zIHcMnZ0fz0ZmHWXT6ISKzRvJq5mBeSh5MSeNZyzyyrBggWTlFlNsNssiMKYz9oQe7i/9FmmM/r6bezZKfx7Is5yGW5f+NFYUjWHXuflaXDmN12V+JKhtGVNkwVpX9lZXFw1lRNILl+Q/ySc6DfHRmFIuyRhF5ciRvZ/6NNzPvZ3baIJ5N+AM/1xsfXcg9V47XH2wLMidpFE/9eAcVQg7Lsp7j9fShfHTmIT4+M4rIrKG8ljGAeSf78eaZfszP7sc/z/bln9n9mH+mH29m9eO1jP7MPfF7ZibfxvSE3zP1UD+eiruVxw/cxIT9fRj+764Mje3M6bqjAOQVl+MPhgyQvHPllFQapnkhfgRf5y7haNV3PJvwe945+QDvnnyAf2bcQ0zR22S7fySzbj+Z7n2cdO/jZP1eTtbvI9O9j8y6fWS49pHm3Edq9T6OV+0luXIPRyt2k1C+i/jybzhYupUfSrfjV40wX1zhQNN0A8RR66bEbjjr5p+Xc9IRz9snJvJ88h94O/N+3s64j1dS7mJLXuT/v0e/vT6qa93nnVXXdXLOlRtJi6qSWLmL8Yd682rGvcxLG8qbJ4byRuoQXj56L+UNuRcdoDHk5kDJNvaXbuZA2Rb2l21hX9nX7C3byNaCKJKr4pqnbw3+QLBlQDttxn4dPsh4hseP3sjcjP/m5dRBvJYymNdTBjMtvh8rT865KMja0wsZvqsr4/f3ZNyBnjx88CpGxV3BiAM2bt5iY8PPKwz/KKlsG1lDooTgCRJAYPKR/jyb2pcZaQOZdfyPzDn+J145/l/MSfojU364lW05yzuE2F30FU/80JcZSQN5+fifmH38Tmam3MGM1Dt4LOE6pv90DyHFCGLZ+SXtL3pn88rJCR5hbPy1TEq6jqnJv2XGsduZdfwPzD52By8n/5FZSX9g8sGbeDflGU7VHMUTqKMhVM/Z2hMsSZ/JY3G38I/E25l77I/MOv4HZhy/nRkptzE1+Rbu+beNhMrvjLEKSi+cj0T+8AaPZfTi2eQ/8Gj89fz96C3MSB7AjOTfMzPpNmYn3c7MowN4/IdrmXKoPy8l3MtLP/2Fpw7fxiMHr+HFxH7MTrqNmUm/56XkAbyY3I/pyb/jr/tsRJ542hqnOGydaRdkxv6J/DXBxvflX/Fd0Rru2W3jmYTrmZnUnxlJ/ZhxtB+zjg5g1tH+vJBwM1Pjr+fZ+Ot5PuFmZiX2Y9ZR43kvJfXlpeR+PJ90M3/da2Nm4gME5MZ2c5F2QV5OeYA/7rZxrOYgANE/f8S933ViTFxXZiX1ZfbRvsxK7MusxFuZnXgrs8JkZuKtzEi8lZlHf8espL48Gd+Lu3fbeDVpLA1BY+kvc7T7ZTktQUQlyLQjg/nzv20cyo+zrsdX7OKR/bcybLeNp368hpd+uok5ib9lbuKtzfI75ib+jtmJv2PGT7fwfMJveOj7zty/O4JPs+YjKsYUdTc04Wloav/YRusLz/04lAeTIsgsPENewfnc0is1EH32Qx47MICH913F+O//F+O/t/H4wW7877gIHtnfhXHfd2Hs91cyZl9v3k19lgLPaev15TV1NDR6Oz4/0vrC26ceYVhcL45X/GB4d2Eljf7Q+cJI9pHqiGPVqVeZd+wRZicOZ0bCvbyS9DAfpD3PnnNfUuO3ny9NVI2T+eVI8oVPGrUB2ZsfzX1HehJTtMK6FgyJZOYW0+gXf1EIz8wro9LhvKTn2lrugkMoFOS5n/7C0APXYK8tb3GepMrh5HB8Cmknz5JbXEpBSQVlFbVU2N0UlVWRW1RK1s95/JCQzMkzPyPLMpqm4ff7CQQChEIhJElCURQ0TcPsVOm6fn6tCT+oUOkq4bGE27n/qzv4KeMIwVCQQCBAbV0ttXW1VFRWkJ19lrS0DJKSj5F4NInk5OOcPHmK/Px8amtrcbvd1NTU4HQ6cbvdNDU1WTCyLKOqaguYDmvfgNzE4bIYdpWv4UT5EezuMkQl9B877PT/DQC7cLwx8LR3hQAAAABJRU5ErkJggg==) no-repeat;padding-left:40px}
-      .browser .browser-firefox{background-position:0 -34px}
-      .browser .browser-ie{background-position:0 -68px;margin-left:0px}
-      .browser .browser-360{background-position:0 -170px;margin-left: -27px}
+      html,
+      body,
+      div,
+      span,
+      applet,
+      object,
+      iframe,
+      h1,
+      h2,
+      h3,
+      h4,
+      h5,
+      h6,
+      p,
+      blockquote,
+      pre,
+      a,
+      abbr,
+      acronym,
+      address,
+      big,
+      cite,
+      code,
+      del,
+      dfn,
+      em,
+      img,
+      ins,
+      kbd,
+      q,
+      s,
+      samp,
+      small,
+      strike,
+      strong,
+      sub,
+      sup,
+      tt,
+      var,
+      b,
+      u,
+      i,
+      center,
+      dl,
+      dt,
+      dd,
+      ol,
+      ul,
+      li,
+      fieldset,
+      form,
+      label,
+      legend,
+      table,
+      caption,
+      tbody,
+      tfoot,
+      thead,
+      tr,
+      th,
+      td,
+      article,
+      aside,
+      canvas,
+      details,
+      embed,
+      figure,
+      figcaption,
+      footer,
+      header,
+      hgroup,
+      menu,
+      nav,
+      output,
+      ruby,
+      section,
+      summary,
+      time,
+      mark,
+      audio,
+      video {
+        border: 0;
+        font-size: 100%;
+        font: inherit;
+        vertical-align: baseline;
+        margin: 0;
+        padding: 0;
+      }
+      article,
+      aside,
+      details,
+      figcaption,
+      figure,
+      footer,
+      header,
+      hgroup,
+      menu,
+      nav,
+      section {
+        display: block;
+      }
+      body {
+        line-height: 1;
+      }
+      ol,
+      ul {
+        list-style: none;
+      }
+      blockquote,
+      q {
+        quotes: none;
+      }
+      blockquote:before,
+      blockquote:after,
+      q:before,
+      q:after {
+        content: none;
+      }
+      table {
+        border-collapse: collapse;
+        border-spacing: 0;
+      }
+      a {
+        text-decoration: none;
+        color: #0072c6;
+      }
+      a:hover {
+        text-decoration: none;
+        color: #004d8c;
+      }
+      body {
+        width: 960px;
+        margin: 0 auto;
+        padding: 10px;
+        font-size: 14px;
+        line-height: 24px;
+        color: #454545;
+        font-family: 'Microsoft YaHei UI', 'Microsoft YaHei', DengXian, SimSun, 'Segoe UI', Tahoma, Helvetica, sans-serif;
+        overflow-y: scroll;
+      }
+      h1 {
+        font-size: 40px;
+        line-height: 80px;
+        font-weight: 100;
+        margin-bottom: 10px;
+      }
+      h2 {
+        font-size: 20px;
+        line-height: 25px;
+        font-weight: 100;
+        margin: 10px 0;
+      }
+      em {
+        color: red;
+      }
+      p {
+        margin-bottom: 10px;
+      }
+      hr {
+        margin: 20px 0;
+        border: 0;
+        border-top: 1px solid #dadada;
+      }
+      span {
+        display: block;
+        font-size: 12px;
+        line-height: 12px;
+      }
+      .clean {
+        clear: both;
+      }
+      .browser {
+        padding: 10px 10px;
+      }
+      .browser li {
+        width: auto;
+        padding: 0 80px;
+        margin-top: 30px;
+        height: 34px;
+        line-height: 22px;
+        float: left;
+        list-style: none;
+        background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACIAAADMCAYAAAAWCXEwAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKTWlDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVN3WJP3Fj7f92UPVkLY8LGXbIEAIiOsCMgQWaIQkgBhhBASQMWFiApWFBURnEhVxILVCkidiOKgKLhnQYqIWotVXDjuH9yntX167+3t+9f7vOec5/zOec8PgBESJpHmomoAOVKFPDrYH49PSMTJvYACFUjgBCAQ5svCZwXFAADwA3l4fnSwP/wBr28AAgBw1S4kEsfh/4O6UCZXACCRAOAiEucLAZBSAMguVMgUAMgYALBTs2QKAJQAAGx5fEIiAKoNAOz0ST4FANipk9wXANiiHKkIAI0BAJkoRyQCQLsAYFWBUiwCwMIAoKxAIi4EwK4BgFm2MkcCgL0FAHaOWJAPQGAAgJlCLMwAIDgCAEMeE80DIEwDoDDSv+CpX3CFuEgBAMDLlc2XS9IzFLiV0Bp38vDg4iHiwmyxQmEXKRBmCeQinJebIxNI5wNMzgwAABr50cH+OD+Q5+bk4eZm52zv9MWi/mvwbyI+IfHf/ryMAgQAEE7P79pf5eXWA3DHAbB1v2upWwDaVgBo3/ldM9sJoFoK0Hr5i3k4/EAenqFQyDwdHAoLC+0lYqG9MOOLPv8z4W/gi372/EAe/tt68ABxmkCZrcCjg/1xYW52rlKO58sEQjFu9+cj/seFf/2OKdHiNLFcLBWK8ViJuFAiTcd5uVKRRCHJleIS6X8y8R+W/QmTdw0ArIZPwE62B7XLbMB+7gECiw5Y0nYAQH7zLYwaC5EAEGc0Mnn3AACTv/mPQCsBAM2XpOMAALzoGFyolBdMxggAAESggSqwQQcMwRSswA6cwR28wBcCYQZEQAwkwDwQQgbkgBwKoRiWQRlUwDrYBLWwAxqgEZrhELTBMTgN5+ASXIHrcBcGYBiewhi8hgkEQcgIE2EhOogRYo7YIs4IF5mOBCJhSDSSgKQg6YgUUSLFyHKkAqlCapFdSCPyLXIUOY1cQPqQ28ggMor8irxHMZSBslED1AJ1QLmoHxqKxqBz0XQ0D12AlqJr0Rq0Hj2AtqKn0UvodXQAfYqOY4DRMQ5mjNlhXIyHRWCJWBomxxZj5Vg1Vo81Yx1YN3YVG8CeYe8IJAKLgBPsCF6EEMJsgpCQR1hMWEOoJewjtBK6CFcJg4Qxwicik6hPtCV6EvnEeGI6sZBYRqwm7iEeIZ4lXicOE1+TSCQOyZLkTgohJZAySQtJa0jbSC2kU6Q+0hBpnEwm65Btyd7kCLKArCCXkbeQD5BPkvvJw+S3FDrFiOJMCaIkUqSUEko1ZT/lBKWfMkKZoKpRzame1AiqiDqfWkltoHZQL1OHqRM0dZolzZsWQ8ukLaPV0JppZ2n3aC/pdLoJ3YMeRZfQl9Jr6Afp5+mD9HcMDYYNg8dIYigZaxl7GacYtxkvmUymBdOXmchUMNcyG5lnmA+Yb1VYKvYqfBWRyhKVOpVWlX6V56pUVXNVP9V5qgtUq1UPq15WfaZGVbNQ46kJ1Bar1akdVbupNq7OUndSj1DPUV+jvl/9gvpjDbKGhUaghkijVGO3xhmNIRbGMmXxWELWclYD6yxrmE1iW7L57Ex2Bfsbdi97TFNDc6pmrGaRZp3mcc0BDsax4PA52ZxKziHODc57LQMtPy2x1mqtZq1+rTfaetq+2mLtcu0W7eva73VwnUCdLJ31Om0693UJuja6UbqFutt1z+o+02PreekJ9cr1Dund0Uf1bfSj9Rfq79bv0R83MDQINpAZbDE4Y/DMkGPoa5hpuNHwhOGoEctoupHEaKPRSaMnuCbuh2fjNXgXPmasbxxirDTeZdxrPGFiaTLbpMSkxeS+Kc2Ua5pmutG003TMzMgs3KzYrMnsjjnVnGueYb7ZvNv8jYWlRZzFSos2i8eW2pZ8ywWWTZb3rJhWPlZ5VvVW16xJ1lzrLOtt1ldsUBtXmwybOpvLtqitm63Edptt3xTiFI8p0in1U27aMez87ArsmuwG7Tn2YfYl9m32zx3MHBId1jt0O3xydHXMdmxwvOuk4TTDqcSpw+lXZxtnoXOd8zUXpkuQyxKXdpcXU22niqdun3rLleUa7rrStdP1o5u7m9yt2W3U3cw9xX2r+00umxvJXcM970H08PdY4nHM452nm6fC85DnL152Xlle+70eT7OcJp7WMG3I28Rb4L3Le2A6Pj1l+s7pAz7GPgKfep+Hvqa+It89viN+1n6Zfgf8nvs7+sv9j/i/4XnyFvFOBWABwQHlAb2BGoGzA2sDHwSZBKUHNQWNBbsGLww+FUIMCQ1ZH3KTb8AX8hv5YzPcZyya0RXKCJ0VWhv6MMwmTB7WEY6GzwjfEH5vpvlM6cy2CIjgR2yIuB9pGZkX+X0UKSoyqi7qUbRTdHF09yzWrORZ+2e9jvGPqYy5O9tqtnJ2Z6xqbFJsY+ybuIC4qriBeIf4RfGXEnQTJAntieTE2MQ9ieNzAudsmjOc5JpUlnRjruXcorkX5unOy553PFk1WZB8OIWYEpeyP+WDIEJQLxhP5aduTR0T8oSbhU9FvqKNolGxt7hKPJLmnVaV9jjdO31D+miGT0Z1xjMJT1IreZEZkrkj801WRNberM/ZcdktOZSclJyjUg1plrQr1zC3KLdPZisrkw3keeZtyhuTh8r35CP5c/PbFWyFTNGjtFKuUA4WTC+oK3hbGFt4uEi9SFrUM99m/ur5IwuCFny9kLBQuLCz2Lh4WfHgIr9FuxYji1MXdy4xXVK6ZHhp8NJ9y2jLspb9UOJYUlXyannc8o5Sg9KlpUMrglc0lamUycturvRauWMVYZVkVe9ql9VbVn8qF5VfrHCsqK74sEa45uJXTl/VfPV5bdra3kq3yu3rSOuk626s91m/r0q9akHV0IbwDa0b8Y3lG19tSt50oXpq9Y7NtM3KzQM1YTXtW8y2rNvyoTaj9nqdf13LVv2tq7e+2Sba1r/dd3vzDoMdFTve75TsvLUreFdrvUV99W7S7oLdjxpiG7q/5n7duEd3T8Wej3ulewf2Re/ranRvbNyvv7+yCW1SNo0eSDpw5ZuAb9qb7Zp3tXBaKg7CQeXBJ9+mfHvjUOihzsPcw83fmX+39QjrSHkr0jq/dawto22gPaG97+iMo50dXh1Hvrf/fu8x42N1xzWPV56gnSg98fnkgpPjp2Snnp1OPz3Umdx590z8mWtdUV29Z0PPnj8XdO5Mt1/3yfPe549d8Lxw9CL3Ytslt0utPa49R35w/eFIr1tv62X3y+1XPK509E3rO9Hv03/6asDVc9f41y5dn3m978bsG7duJt0cuCW69fh29u0XdwruTNxdeo94r/y+2v3qB/oP6n+0/rFlwG3g+GDAYM/DWQ/vDgmHnv6U/9OH4dJHzEfVI0YjjY+dHx8bDRq98mTOk+GnsqcTz8p+Vv9563Or59/94vtLz1j82PAL+YvPv655qfNy76uprzrHI8cfvM55PfGm/K3O233vuO+638e9H5ko/ED+UPPR+mPHp9BP9z7nfP78L/eE8/sl0p8zAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAC7ESURBVHja5Lx5dFRV1rBfgHwYRQQVtB26ZWhtabtfeUGxGxFbUGZF8RMHGkVbRkekVYiKisicVhE0gEwBokgDAhEMMSSQkAECwcxkrlRSqVTqJqnxzs/vj5t7qUyAvr9e37fWV2vtleSm6p6n9t5nn733OVU2RaUaEP5PiqJSbeMXPBTA5/Xhzk9Vnd9vo3HFx21E2LYJX9IRgh6npvyCe9uaqS4K4C3IpXHFx9S99CTuJ8Z0KLVjRlA7ZgTuJ8ZgXxmJL+kIlwAkXBQk6HFq9pWRVA8fSvXwodYgdS892a6EA1UNvouqwXdR99KTeAtyfz2IL+kI1cOHYh9wqwVwKWJqpXbMCOv19gG3Imzb1JF2OgZxfr/NukH4jcNVfyEAE8IU+4BbKet1PfaVke3BtA/i/H6b8aIBt7a4mWmaC0nr55vmqRp8F5V33Mm5LhHtwbQF8SUdsSDCb1I1+K42g1xIWgOYYh9wK+e6RCBs29QxSIWus37aJM51iWjx4so77mwD1d5AHQ1eecedlN9yuyVlva6nrNf14Q7cEmRn4W7u3T2E9ME3UX7L7W1uZg5Weced1s3sA2613ql5LXzQjuRclwjcT4wxTXQeRHC7GLdnHPeensiCVwa3e0PznZk3EbZtwluQa0kofz8NcVNxr++Ce30XnNuv61Bcu7viXt8Fvyu7JYipjfGHxzD+8Bh2j+7fAiZcC+Y0zPDIbCyD6DyV6DyVeDcIQR2C39J4oieNJ3oSOnkVcnZ35Ozu6MVdDHF0N6S4C43OqJYg/0ydzb27hzDx0FjuPT2R+asfa6OVsl7X40s6QoWus/CQk6fWZPHChhxe3lbMCxtyrN9TyxSQSwidvMoC0XK6tRGybPjSRmOuNUKVo4Zxe8YxIu4+Jh4ay/jDY7j39MQWWjnXJYLGFR9Toes8tSaLiavTrIHDxfxfapkCwW8hy9YuhCmhk1fR1FRnaCS1NM4yy8RDYy2tjIkZRXq/HtYsCnqc2sJDTkYsTrU00J6YkEJQR7M/eEGY0MmrcOenqjZA2JmyzTJLuJiOe65LBHUvPUmGR2bE4lQmrk7jqTVZHcrE1WkMWpRIdJ4KnpUXBCHLRl3e16EWIOEaMU00/vAY9na/gsYVH/NdgYe+8w9bMBeSQYsSWXjICcFvL2ga+dhlFwcJ10rjio/ZklprgbSWiavTWvzdd/5hXt5W/OtATC201sq9u4eQ+PVijmSW0nf+YQYtSmTQosR2gUYsTmXQokT6zj9saeRCpmkJ0hxD2gOZeGgsI+Lu45+ps7FXlFmDmDDtSd/5h+k7/zCpZQpa9cwOQciyIR+77LyzFhXlMyZmFOP2jLP8orVWRsTdR2ppHFtSa+k6ZZM1WHvSdcomwyxySceayO4OWTY88TdirygzUkWf18eL2//RQiutYcwYE/Q4tagDOUQ8uo6uUzbRZ3qMJV2nbCLi0XU8tSbrolNXzu6OfOyylgEN4NOkaO5acw/j9ozr0ET37h5imehIZimPL91rAfSZHsOQBfuISS7E7vaTETeX0MmrOoQInbwK+dhlNKWsahni0zPSuGvNPW1M1BrI1NrOwt0WkCn2ijJSS+MYt2ccuQk3oxd36RCi8URPY+HLT1VbgGiSzPsx71laCddMe2Yygf6ZOtuScXvG0XfJn/n8YL+LQnjibyQ34WZ8Xl/bfKSoKL+FVi4EYwKZcu/uIQzaPoExMaPQcrq1ADFX33AI1+6u1OV9HVI6ShU/TYqm75I/dwjTHtDEQ2MZt2ccg7ZPaGGScIDWEBlxc42UoSMQ00StYdoDCgcbtH0Cbx+8p40ZTIBwiFM7RmB3+y+exZvT2YRpDdR6ZoVrw1xRWwN44m/Euf06A6Ki7NLrmnDNmH7TEdSg7RP4/GA/yLK1GdwEKNzSk1M7RlDlqPl1JefOlG2MXTGmXaAxMaMsB/XE34h4tH+7ANlrB7T2iV8OAlDlqOH9mPcsIBPKlF3R16Ad7GwlxoVberYAKCrKv1ghfmkg5sPldLIzZVsLqLErxpC9doAlp3aMICNurlGyVpRdSAu/HqS1Q58rd1JUlI87P1UtKsrHXlGG3e1HCOoov+x2wiX3RxT+o49L1IgutXxVUCfDIxNfLraQDI+M3e3/NdCXbhohqBNfLrIsVzZqmoT6dmXG0SBLTrmJLxd/CVRLECXcDGFaSC1TmHE0yKg4B0P2uxiy38WoOAePHaptAfHYoVqG7HcxcGc5o+IcfFfgsbQUPoYoSa213BbE78oGucTSwpJTbobFFjNgbQHdvi6g8/Z6Om+vZ8h+VxsQE7T/97UMWFvA+Og0UvIryfDIZBQ4CeXvt8a5IAhAY/RImlJWUaHrPHaolhuXFXHN+8e58qNcbomq5P6t3xG973WePLzPgnnsUG0LiP7f1zJwZzk3LisyctfSOFxOJ4lfLzYToQubxu/KpmpWBFWzInguOokrP8ql7/zDRMxLpFfUabasHwlZNnITbmbgznI6b6+3Bu7/fa2lrW5fF9Ar6jQD1hYwLLaYx5fupdi+EiGok748koa4qa010xKkKWUV2UM7kd6vB7tH9yfpnUFkLzQiZOGWnmgHO9N4oie9ok5bA4YPbkqvqNNc8/5xIuYl8tSaLOLLRXambENXF+PxNJD0ziAanVHhYaEliH1lJD/1iqD0qSsIzu2M/N550TZ3QjvYmS3rR1qDtwdhgpgwnabGMj46zRjQsxJdXYw7P1X1pY0GuaRjkMKxPah5qxuV8y6nct7l1LzVDfdyo6miHexM+ou9mblwKfdv/Y77t37HNe8fbwMQDhIxL5FOU2PZklqLJjUYdU7wWxBuN+ricBAF0KQG6pcNovZpw0fCQao/MEBcu7tSOLYHjnu7EZzbmeDczqyfNokrP8ptMXi4XDnzAJ0n72TIgn1oUoMB4VlpgIjj24I0payi9KkrqHj+Ssth2wM5c38f8p68D2nbHKRtc3h86d42A/eZHsOVMw9Y0nXKJmxDvyS1NA70z8Gz0qh5hNvbzpr6ZYMofzyiBUwLkOVdjfR/eVcao0dSl/d1aHx0GhHzEi0TXDnzAJ2mxtJpaixdp2yypM/0GLrcs5D3Y94ztNDsK7qjuxmzDBBz2rYGqZoVQc1b3dr4yfppk+g8eWeLd91aAxGPrqPbyKV0G7mUiEfXMWdz+nmQ0Jsgn1AbT/SkMXrkeZC6vK9DpU9d0S5I5bzLqf6gq6UV7WBn5q9+zDJBuEQ8us4SE6LLPQvpcs9CjmSW4ndlo1XPNBxWLiE34WbSX+wNapEBEsrfT/njERSO7WGBmDA1b3Wj9KkrSO/Xg1WjBjJl/CT+8sQ8a0BT/eGDhwN0uWchXe94ia07YkE+oSLc3gxyQt2yfiSrRg0E+YRqgRSO7UHh2B4UT7ragqmcdznFk67mp14ROO7txpTxk7AN/bLFgN1GLsU29EvrejiACdG59xQjKgu3GzVP9UwIvcmCVwYb102NmBHVBDFNVDUrgjP39yF98E0E5xox5Dcj5lsDhwOYQObg4dK59xR2RV8D4njEo/0NIEd3dkVfgy9t9HkfMTWSO6pXG63kjupF8aSrqXj+SoJzO1M573KmjJ/Eb0bM5y9PzGPBK4Mp3GKUEFvWj+Q3I+a3AOjcewp/eWKesUQ0T1mz2att7oSU9+F5EE2SqXvpSbKHdrIGNmHCoapmRVgh33LezZ3QNncyloGDnVnwyuA2IFvWj0Q+dplREzu6Wy0r9/KubVvg9pWRpPfrwZn7+1haMSHCxdSM/J4RWWufjiC9Xw/m9PgtN9w0uo1JbrhpNI0njAXTrAIbT/TEvb4LjdEj2641vqQjpPfrQfrgm1qYKHxKlz51BbmjerFj4G2WtAYwtWDKglcGG2ZoXrldu43AWDUrAmnbnLaRVZMayHvyPn7qZThoa38pfeoKap+OIDi3M6tGDeSGm0a3GTT82g03jeaGm0bj3H4d8rHLrN0I93LDpDsG3kb68si2a425hfZTrwjSB9/UBiZcM+YM6ghoyvhJpL/Ym+yFhknc67tYQVF+z3gjc3r8Fuf32zpOFTMeHXpRGDNfMYF2j+7PqlEDWTVqIOkv9rZ8SNvcCff6LlTOu9yK1Okv9mZOj9+S8ehQNKmBDhs17vxU9adeES1gwoHKH49oFyhcwhfKynmXWzOu4vkryR7aieyhnQjl7+84QzNNJGzbxN7uV1gw7WmntYZaLw2mmNdrn44ge2gnztzfx9od7zBnDa9t0pdHtgsTDhRustaaCndwEyLj0aG481PVS9r3FSUJj6eBrConMZHvnodpntrh2gkHCgcLl/TBN7G3+xXGLMlIo0LXjU7ixeoaUZIQ3C7OlTtJya8kJvJddgy8DctvWgGFaylcHPd2Y2/3K5jT47esGjWQrTtiyapy4nI6jUrvUmpfUytFRfmkZ6SxdUcs66dNYsfA2ywNtQBrJeb/dgy8jZjId/kx4YgF4fP6Ln1L3uyhhWvnSGYpOw6lEBP5LuunTWLDAw+x4YGHrAi74YGHWD9tEuunTSIm8l227ohtAyBK0i8/pNDagTVJxuf1YXf7OVfuJKvKMF16RhrpGWkcySwlJb+SrCqn1awRgjqaJP9nO0b/Zxo1v+ahS0ZqKJ9QCX5rJMyhN42aRj6h/udB5BKjiAp+i64uNrJ2M0Vs3rUiy4aU92G42X49iCYZDZjUMoX4ctFIcILfGgVU6E0LwEyCxKP98aWNxpc2GvFof+RjlyHlfdjxWnOxh93tJya5kIWHnDx2qJbnopP4NCmaYvtKC0LL6WYkQps70RA3laaUVbjzU1V7RRn2ijK8BbkWUJsM7VIAog7k8MyuPKtD1AJA/9zQQpYN9/oubFk/kpkLl7J4a0KbtrdZa/vSRrfMWS8GcSSzlGd25TH5VIjptTpR9T5SS+OMsrHZD3RHd7SDnTm1YwSzY2KsTtL46DSei07iSGZpm/tKeR8a5gnf0+vI8zfE5zAstpjptTrvifBJeeZ5LTQDkGXDtbsr0fte59mjDmaWaUyv1ZlZpvH3XJlRcQ6Grj5OTHJhy/t7VhrpwMVAog7kMCrOwcs+nZWaccak2L7S0oLpC6d2jGDJiUyWN8E6FVZqsLwJ5ruwYO5O9jFoUSIb4nPOT+/gtxf3kZjkQobFFreAaHRGGZoQbm+hhWd25fHsUQevHilgbo7bAmoNM2S/i6Grj3Mks9Tolcgn1Hb39MzHuXInw9edZrJd4z3xPISuLrYgCrf0ZOuOWKLzVFLLFDIKmlfr5EJmHMxhfoWvDczkUyELxl5RduFUUZNkIvdm8+BpkZd9eocQPyYc6XDnocpRQ+TebObmuFmptdTK5FMhBqwt4K1vMi4cWTMKnIyKczDZrvFJeWaHEBdrbVc5aphxMIflTR1rJaPA2TFI1IEc7k72tZwdYRCLtyZc6h4MMcmF7WrlwRSRAWsLiNyb3T6Iz+vjmV15jIpztIHwxN/I7JgY4svFS47CHk9DG62Y5hm4s5zx0Wntb0CnlikMiy3m06ToFpFSO9iZnSnbeGZXHkcyS8kocF6SHMksZc7m9AuaJyW/si3IltRaZsfEGNM09KZVs2bEzWV5EyzLlXn1SEG7MuNgTruy5JS73dlzd7IvPMi1BIlJLmRnyjbLJFawar7ZHi5NdrSS9jRyd7KPXlGnzQDXyjSlcYY2mk1SuKUnS05kslI7f9M9/HKgdaoh74nn/cR02NV7M9t2A9A/t/qf2uZOvB/zHvNdxk3Mm0bV+36VzK8wxHTWVutPmEbkE6q1hjQ3/yefCvGeeB7k1SPGlLsUeeubDOtnezJnczpvfZPBuXJnGEjzAqSri9FyulG4pSf3b/3OCvErNQNmxsEczpU70ST5kuWXJc9yiZXemQ3du5N9TK/VedmnW1qZm+M+v3r+gpTS42nA42nA5XRa4vE0hFd8zSDBb63cInvtAAYtSuTuZB+T7ZoFYy7tz+zK6+igQZtHRoGTyL3ZLab4M7vyGB+dxpAF+1i8NaEliLmWyNndsa+MZPi60/T/vpaJhTKT7ZqllZWaoZW3vsnA42m4IMS5cifPRScxN8fNeyK87NOZXqszsdDITa55/3i4dgVb0OPUTG2IR/vjzk9Vt6Qau5R3J/uYWCi3MJEJM2dzOkcyS80Q3WKrPia50IIIX2cmnwrxYIpIr6jTPBed1Mo0apFgpv0NcVMR3C5ESWLO5nS6fV3Ag6fFdmHmV/iYcTCHyL3ZRB3IsSRybzbP7MpjfoWvXYj+39cyZME+c7aEgTQ36smy0RA31dostrv9DF193IIJ9xcTxgSam+O2xAQwg9fMMo2JhTIPnjYgBi1KbC+RPq8REyR8iT9X7rRgWptpvssYLBwqHGB6rc7fc2ULYsh+F4MWJbLjUErH09c8ytcaxNTMCxtyGLC2oIUDT6/VO5TJdkMLJsTAneUMWpTYNotvE0eaj3rKxy6zun2t69mdKdt4fOley4lN35ls11pIOIC51D8XnWQu9xcGUQCteibyscuM5n31TKNqD5fm1H9DfA7PRScxdPVxhsUWMyy22Dq4MGS/i2GxxQxfd9oC2HEopb1WVcdtCU2Sqcv7OmTWpGbRLOV9SCh/P0GPUwvPvDIKnMQkFxK5N5s5m9N5LjqJ56KTeOubDFbvzSQlv7LN1P5FxzZ8Xp918v8SWk5WsWStLbr0a5oLHRdY/+GjPP8vtq7+0yCiJOHz+hDcLlxOJ2bzxeV0Irhdlk/9x0B8Xh9VjhoEt6s5rZTaFU1qQHC7qHLU/PpZ05EGqhw1uJxO0CVESSIlv5KoAznM2ZxufTJgzuZ0og7kkJJfaR1mcjmdVDlqflkc6ahSs1eUWdMzJrmQQYsSrYMJNy4raiHmYQWzD2IC2SvKLpa/dAzi8/qsc6cZBU6GLNjHlTMPcEtUJVMSdd45qRGdp7KxDOvDPu+c1JhxNMgtUZVcOfMAQxbss0K7vaLsQqbq+GCtCbEhPodOU2O58qNcZhwNsrMK4t0Xlp1VMONokCs/yqXT1FgrE7sATPvbJK0hblxWxDsnNWugvc7zcqFry3JlbomqbANzSdskpk9kFDjpOmWTpQnzne6sMgbbWWWYY8kpN0tOuYnOU1v8z9TcOyc1blxWRNcpmwwz6dLFjxr7vD5rY+eO13YSMS+Rh/co1iAby4wBluXKLDnl5rsCD1lVxk7FdwUelpxysyxXbvHcjWUwYb9CxLxE7nhtp7X10spELUHMMiHqQA6dJ+9k8KYaJh1u6ZRLTrnZklrb+hS3lURtSa1lySm39fyNZTAlUWfwpho6T95p1rqtS5LzICapJsmWNkbEBpiSqLMs1/gY3DsntfAuT4tDlkrYtci92bxzUmNjmaG9KYk6I2IDbbTStsBqjhma1EBKfiVdp2xiwNoCHt6jMOmwxjsnNev46KWUkaIksfCQk2W5Mu+c1Jh0WGPCfoUBawvoOmWT1d4Miy3nQczIuXpvJp2mxjJ4Uw0T9hsg09KM6fhcdBIxyYWXJM9FJzHjaJBpaTDpsAEzeFMNnabGGhVec+RtA1LlqAFd4vGley0Q8wZTEnWmpWGdWX3sUC3PHnW0K+b/n0qoZ1oaTEszfCQc5PGle0GXwv0k7PxI87S9EMjMMo35rvMdILPDbErrzlA4iOmw4SBh0/iXgUxLg8mnQvw9V2Zmmdau/D1XtpoxpiYe3qPw8B6FW6IqreOCvwpkWhqMinMwaFEi46PTfrFMXG38HLr6OHe8ttPykXZNYzrr4q0JdJoay4C1BS2cdfCmGuZsTrd6Hv/T5ozZJ7no9L1xWZE1fU0bD193unXx3GESFZNcyIb4nDazaUN8Dh6PkTy1O307CmgT9itM2K9YWnkuOumi26wTV6dZR43NXOXKj3LpPHknEY+us0DaDWiWnwCr92bSdcomBm+q4eE9ShsThTXh2jRn5mxOZ/CmmjYzZkRsgE5TY40Q33bhu/iiF66VcJjh604TuTfbUnnk3myGrzttQZgzZtJhzQrvfabHWGNccNELnz2tfSUcJjxADVhbwIC1BdYsMyOp+fyH9yhWGnAks/TS0gDTV4qK8q2NxU5TY7klqrIFTDhQ6+gZ/hwzdoSbpKgo/9LPj5hnR8yUwEwVw810MRkRG7BSRXPpLyrKv/RUsT2YI5mlLZLnEbEBK1q2lhGxASt5vuO1nZY5ioryL5TJX7icENwuioryjV1rr4+oAzkMWbDvouXEkAX7iDqQg8/rQ5MaLgZxaQWWJslWSWkWWBkFzl9UYP2PvgjFPNrj8/osM/2YcIQfE46QnpFmfL7K7SLocWpBj1Mz6+D0jLQWzzPb3b/6aI8SVnCbvXTTVOZxno6kqCjfKlPNUH4pIP9XPGz/N319UFnrf2iKLGi6LmggqCBoIOi6JuiqIqCrgqIrgqyrgoYu6JpiiK4LKgigCpquCCEdQdVVAU0VdP2iMGW29tplmtbcQNQ1QEXXNDQdQGsWHZBbvdQsKkTQfaiaBJrc/PyLPpQ2zqqbL9U10GV0TUbTZUCyQAoaJPaVinx5RmbVKZnVWRpf56r8WKlQFww2Q4bf8VdMXwsEtfkdGb97xSAb8yRG7df4zYYQ3deEsK2WsK1UsK1U6LIqxJWfKQzcEODVw0GS7KbG1F8Pout6C7WuL5Dpv1PBtlLEFgWXfyHTY61Ery91rvkiwLWfB7h6jcxV/5LoskLF9gl0+tjLI7FesuuxzKnrHeqneQdL143Bjacj6wqg4ZFUph8JYvusCdsXIldvhGvXi/T+SuS6dQrXrZO4fp3Ib76UuH5NiD6fi1z/mcgNnwa5epWMbbHG1StEvsoSjbeoq2i60h6MYNN1XTAhNF1vdlBoVFSG7/Nh+1Ti2o1Brl8v03uDyDVfN3DDVz5u+FKh15cKvdbp9FoHvT5X6PW5wjVr4LrPda6NkugTJdL1EwXbIpkVx5sdGaXZ8S9gGgNIJ6ipPHgghO3TED23h+ixTafXZpmb1ofos0ml+9dw1VcaV3wapMvKIF1WSVz+qULPzxV6faZw9Wc613yq0Xt1iN9Ehei+WMG2QObz03JHDtxsGk07P2XRmZ/hx7ZG5rqtMjdubqTHFonrNov8doPMZRvA9pmPqz8X+MNWhb/tkrg/VuGWaJXLPmmk85Imen6m0+sz6BMlcsNqP9etVujysU63jwIcrwy1N6UFm6Zrgma4KKBxrE7lyq999PnaT58dcMNWjV5bFa7d6sP2lcj/+szP6/FNHK2SqQtpSKqIKItUN2psyJH52yYXtkV+uq9UuP5fMj1XqVy9WuWGFSE6LQgxbHMQv6kVXW92B12wKZouSEjGNNMVJvwgYdugcGOsym+2q/TZqnD9dh3bVz5u3h4guVJtnpJa808zkJlBMMS7SQG6vB/gimUKvVdK9Fmu0nu5zLXLZGzvaWzLDhggmoysqwYIKoKqG+rKqVO5douP62JUfvutxg2xCn1iZTpv0rgpRuF0XQAIgRJElSUURUWWZWRZRpFlgrIKeIEg7yaC7X2FXkslei+XDVkmY1sQ4pFNDaA3hwcdNF0XbGjNZwNQWXZaxrZV5XexMjftFLnpW4ne34rYNvjZUywBQUJqEEkMoEk6oqIgySqipCCKEt6Qis8fRNEaAB+TtijYInV6Lwtx7VKRPstkIj5S6PGBRGFtwFCgApquCDYFTQANXZeZkiARsVPnlu9kfhcr0/cbiYivA4w94DM0oet4VQVJUQiJGiFRIiTKBEMSAX+QhoBIvU/C1SQCfpIKGrl8kZerFitcu0Tkuk9ErlsiYXtDYuMpYyobE0gVbIouC6DiDsgMiwtx406Z/rs0+u6WGPCNSI8tIZbnSoCCEvITkBRkWSMUkAgEJbz+EE2+IA3eAPUNjTR6fNTWSni9PuoFN/d8KtBpkcg1n3jp82GQ3h/6sc33seAHb/P6pYOmCTY0VQCNEkHhrgMhfrdL5k/fafT/XqT/boU+sRI/2r0AhESFYFDCF1TwBSWa/CE8TQHcjQFcDX6cdQGq63w43PWU1AoEmup4emMjtvl+enzop/d7Aa57N4Btvo/Z37jCHBzBpuqaAHDOHWDo/iD99in8+XuZO/er/H6fxsB/h0irDgGqoYGAhOAL0eALUd/oo87TRK2nCUddI3anQKXTTUl1DUVVNXga6nh2mwvb6066L3Bz3btOekU2YXtd5MVNDmuVVtEFm6brAmiUu4OMPODnjv0idx+UGHpQ4q6DEnf928+h0iCg0egN0OgXqW8MUCd4cXm81LgbqHIJlDs9lFd5KK90U1hWQ3GlgLOqlrs/rsQ2q45rFjq57q0yukc6sL3iYc62akBDR0fRNcGmq5oAQYSAyuQEibsPhnjgkMYD8T4ePOTnv/ZrfJrtBTWE4A3ibvRTJ3hx1jdRXddApbOeMoebEruL3Ao3p8vqOVVSR1JuDZkFtbywvgDb0zl0eqmanm+Wct2bFdhmlvP2Po/hH6qIrmiCTdNUAVVElTVeyfTz10My435UGHNE5JGfJIYf1ZiV4kFo8uILBKirD+LwBHC43Dhq6ymurqfAUU9ORS05RSU0NHmQVRW/JCMqOho6354U6DEri04z8+nxWim2fxSx8ZgLEAlJCqoiCzZZUwVZVECDjUVNDD8s8sRRlSmJOs8mwbPHZJ466iO2yI8aDNJU56a8tpGqaicOZx2FVfWcLa8lq7CMBn8IHfAGZQKSik/SQAoBOjEZtdiezqTTS/l0fzmPrFIBNB9CUCcoSYJN0TTBKymgS5TXBXn8pwCTj8lMT1WZmarx+nGR2Rk680+GOFleh9/bgMtZR3V1HYWVLvJKajiTV0pVjRsV8IVEgrLaLApeERSCAAx5/xS2+48zZvlZAmKIQFMTHq+PppAi2DRdFQIyyKIfRImoMz6ePO7lpUyJl08r/PN0iMjTEh9kS6zNEUgp92GvaaDAXstZh4DLG0JoChAMyviDMn5Jxi/K+EISIVnFr0h4JWPZ33a8mNteSCI6vhpZbMDhaqChyYfHHxRsmhYURBECoRDoMvkukVfTFN7IlHk7W+aDXIlVOSHW5ob4qhi2F4v8WNLIiSov5wLgkVVERSUYMqa2LyTjF1UCkkpQ1vGLImJAxCsai2SdKFJQ6aG0ooqK+gBuVxOCTxBsuq4IkqQSFCVCkgyqzg8lXt5J9/H+WViVJ7G+KMSOEoVdJSp77DJxdRrH3Rq5goLDJyMERRqCIt6QbPiHqBAQFSRJJSCrhGSZJklDUs/nIefsNRRXe3DWefE0NjUf21BURFEiGDRWVH9I5Nu8Rt7Pk/lXocbWIpFvKzT2VSr8YJdIcEqk1Svke2TsPhV3SMYTEmkISngDCr6QTFBSCUkqQUnFL2kEJUNLflFF1aGuyUepow6HuxG34DdyVkVRkCQFUVLxBWR0ScEfFPmuuIG1hTIxpSr/rpA46FBIqJHJdGmcqVPJa1Co9MrUBiTcQQlPQKYhoNAUUvCJCn5JJSApBCTZEr8oEVJU/IpKiaOOmnov9Q1+QyOqqiErGqKiIYk6/mAATQ4QalRItPvZU+EnvkrmxxqJRJdIVp1KTr1GQaNChVei2idTE9BwBRTqAzKeoEyjKNMkKvglhaCkNAMZogAeX4DS6npcDQE8jYHmM0aajqLqyLJOSNbwSTJev0woEKCxyU9OdZCEkgAJ1UGSBYWsBo3cRihq0qj0KVT5ZBwBjdqQRn1IRhBVGiWVRlklqOiIikZQ1hAV4ytjJE2n0ummqt6LU/AjNAYEm64jaBqoqo6iaEiKhiirBESVhkAQr9eH0ChSUu3nVGkdGY4mUmt8ZLoC5DWoFDUplHpVKnw6VT6ZWn+IuqCEJ6TQEFINzUgSflXFJ8nUe304XALVdQ3UNwaob/TT5A0ZILoO4TCyrBKSZHxBGcEfxNPgpdETwO32U+ZoIKesnrPlHrLtbn6urCfPXk+B3U2R3cO5qgbOVTVQUilwrkKgtEqguLKe4sp6yhwNlNg9VLkEhKYgjd4QTX6RYFA+X2Dpuo6maaiqiqqqKIqGKKn4QwrekERjIIC70YenMUBjk0S9EKK23our3ovb48Xj8SI0BfD4ROq9IdyNQeoa/Lg8AZxuPzV1PuobRASfguAN0egP4Q1KBEMykqwKNkAxMnpDNM1oSxhQGrKiI6oqTapIkyTiDYUIiDLBkEwoICOGjHghKxqKqqCoEooqEVJFgkqIkBIiKIsEpBB+MYA/FMAXkgiICiHRmK2KoilWo6bZRIKu61bjRdd1QdEQVBVBkzRBlVRBFhVBVTRBUXRBknVB1hAUECQQNF0XUHVB13RB0XRBVDRBUjRBUTVBUlRBlBRBlGQhJGuCJOuCouiCpuqCqqpl/7Eemqor5HnS2Ja/hPezpvCP1PuYlfo3vvo5EnfA0baH9qs+CKZpBIIh7DUuyuw1lNprqHDU4mnwoqoamq5xyn2YVTkv8cKJO3n+TH+eTB7Ao/H9eSr+TnbmrfyfgdiddZzKKaK0yklhuYN6oWVfvabay+6Tu3gzaSJPpPZm9E9XMmnvH1n60wKSanZypuEg35WuZlrCMLb9vPSXgzicdWTkFLX7vya5Dq/spk62s8v1AW+cu53ns29kSd6z/Fi9mZ/L8tpqVFfZeHYxBe7MSwdJy85v8Xd1oJwDFRtZlTeTD88+wcKsMSzMGsv8rL8wNbMnc7LuJN6xg6AcsF6TW1xBkzfQct9P8pDrSkfT1QuDKKrKz8UV1t+V3kKi89/m1YyhvHlyMPOz/ouFZ4fwYe59fJAzjLfO3s66wuep8p7jbF0iUTkzOe76/rzZ6jxUVteGtch06gL2C4PIikJFtcv6e3/ZeuamDOHNU//NivwxfFY8jnXlE/iyYiKflz/Eh4WD2Gv/CL/YQIJjI2+dvJvXTt7FtJS+LPt5OvVBY383KEoUlFaGzSz5wqb5ubC0WSsyG3PfZUbKnXzw8wOsKX6EdWUT+NI+nq8cY1nrGMnikjuJd0Xhld1sr3iTt37+IyuLHmZN0WMszxnPzLSBvJnxMMWNPxv7vUITLrdw8VlzMswnNud+xD+O3cGy3LF8ce5R1pZN4IuKsXzlGM0X1SP4uPJ2jgpraJAcfFb+CJHnbuOz8pF8UT6OL0om8nnRJFblPcrLaXfxxolROHzGd2idq7xIHBEavTQFQwAcLNvMP5Lu5JOcsawpmsRnJROIKnuYtVWjWVP9Vz6q7McRz0pUTSa2Zh6LSgeytOJPfGa/j3UVY1lTMoFPz01kdcEjLM95hNmp/8UHmU+j6MYnlrJyz3UMknHW0IbDW8rLyfexIGs4nxU8zqqi8Xx07gGiKkfyheN+ltnvJEFYGdYOFWlUqjniWcGK8iFElQ1jTek4Pi2awOqCR1iZ9wgfnx3Hs4l9+aHc+BqH2voGRFFqC+JpaEKSjOR2Y84iZqX8majcx1ieN57Xc+/hvXPD+aziAZaX30VGY0yH0/1s00E+KR7KquL7+ezceFbnT2BFzkSW5Uzg7VP38UbKQ3hCdc1aKWoLktHsG06/nbnJ9/H+6VGsyJnIC9l38kreMNaUPsKSkkHsdy26aABMcK3lw4L/5l9FY1mdP56lOeP55Ox4Psh+mOeT7+BAyUZj17O8qiWIKMkUlNoBOFQaw4zkQSw+M5bZp+7in7mPsKnkFVade4DPSsfTJNVeFCSk+lhbPIVl+Q+wMnccS8+OY/GZsXxwZjTTj9/OkqwXACi3O/H5A+dBKhy1lFQac33t2bf5R/KdvJnxFxadnkSyYzuf5j3BssIR/Kt4DBvLp/NF2dOsqXiSNRVPsKbyCeNnxZN8XjaFz4ufJrr4Bf5V8Agr8h5iWc5YPs4ey4enR/P+6YeYnfZn3kh9CAUfqgz2Gtd5kLOFpZTYjUMHH516jmlJA3jjxHCO2XexteBtFpwZyqqC0awo+huLCv7Eu4W38V7x73mvtD/vl/Xl/bJ+vFfye94tuo2F+X/g3dw/szT/b6zIHcMnZ0fz0ZmHWXT6ISKzRvJq5mBeSh5MSeNZyzyyrBggWTlFlNsNssiMKYz9oQe7i/9FmmM/r6bezZKfx7Is5yGW5f+NFYUjWHXuflaXDmN12V+JKhtGVNkwVpX9lZXFw1lRNILl+Q/ySc6DfHRmFIuyRhF5ciRvZ/6NNzPvZ3baIJ5N+AM/1xsfXcg9V47XH2wLMidpFE/9eAcVQg7Lsp7j9fShfHTmIT4+M4rIrKG8ljGAeSf78eaZfszP7sc/z/bln9n9mH+mH29m9eO1jP7MPfF7ZibfxvSE3zP1UD+eiruVxw/cxIT9fRj+764Mje3M6bqjAOQVl+MPhgyQvHPllFQapnkhfgRf5y7haNV3PJvwe945+QDvnnyAf2bcQ0zR22S7fySzbj+Z7n2cdO/jZP1eTtbvI9O9j8y6fWS49pHm3Edq9T6OV+0luXIPRyt2k1C+i/jybzhYupUfSrfjV40wX1zhQNN0A8RR66bEbjjr5p+Xc9IRz9snJvJ88h94O/N+3s64j1dS7mJLXuT/v0e/vT6qa93nnVXXdXLOlRtJi6qSWLmL8Yd682rGvcxLG8qbJ4byRuoQXj56L+UNuRcdoDHk5kDJNvaXbuZA2Rb2l21hX9nX7C3byNaCKJKr4pqnbw3+QLBlQDttxn4dPsh4hseP3sjcjP/m5dRBvJYymNdTBjMtvh8rT865KMja0wsZvqsr4/f3ZNyBnjx88CpGxV3BiAM2bt5iY8PPKwz/KKlsG1lDooTgCRJAYPKR/jyb2pcZaQOZdfyPzDn+J145/l/MSfojU364lW05yzuE2F30FU/80JcZSQN5+fifmH38Tmam3MGM1Dt4LOE6pv90DyHFCGLZ+SXtL3pn88rJCR5hbPy1TEq6jqnJv2XGsduZdfwPzD52By8n/5FZSX9g8sGbeDflGU7VHMUTqKMhVM/Z2hMsSZ/JY3G38I/E25l77I/MOv4HZhy/nRkptzE1+Rbu+beNhMrvjLEKSi+cj0T+8AaPZfTi2eQ/8Gj89fz96C3MSB7AjOTfMzPpNmYn3c7MowN4/IdrmXKoPy8l3MtLP/2Fpw7fxiMHr+HFxH7MTrqNmUm/56XkAbyY3I/pyb/jr/tsRJ542hqnOGydaRdkxv6J/DXBxvflX/Fd0Rru2W3jmYTrmZnUnxlJ/ZhxtB+zjg5g1tH+vJBwM1Pjr+fZ+Ot5PuFmZiX2Y9ZR43kvJfXlpeR+PJ90M3/da2Nm4gME5MZ2c5F2QV5OeYA/7rZxrOYgANE/f8S933ViTFxXZiX1ZfbRvsxK7MusxFuZnXgrs8JkZuKtzEi8lZlHf8espL48Gd+Lu3fbeDVpLA1BY+kvc7T7ZTktQUQlyLQjg/nzv20cyo+zrsdX7OKR/bcybLeNp368hpd+uok5ib9lbuKtzfI75ib+jtmJv2PGT7fwfMJveOj7zty/O4JPs+YjKsYUdTc04Wloav/YRusLz/04lAeTIsgsPENewfnc0is1EH32Qx47MICH913F+O//F+O/t/H4wW7877gIHtnfhXHfd2Hs91cyZl9v3k19lgLPaev15TV1NDR6Oz4/0vrC26ceYVhcL45X/GB4d2Eljf7Q+cJI9pHqiGPVqVeZd+wRZicOZ0bCvbyS9DAfpD3PnnNfUuO3ny9NVI2T+eVI8oVPGrUB2ZsfzX1HehJTtMK6FgyJZOYW0+gXf1EIz8wro9LhvKTn2lrugkMoFOS5n/7C0APXYK8tb3GepMrh5HB8Cmknz5JbXEpBSQVlFbVU2N0UlVWRW1RK1s95/JCQzMkzPyPLMpqm4ff7CQQChEIhJElCURQ0TcPsVOm6fn6tCT+oUOkq4bGE27n/qzv4KeMIwVCQQCBAbV0ttXW1VFRWkJ19lrS0DJKSj5F4NInk5OOcPHmK/Px8amtrcbvd1NTU4HQ6cbvdNDU1WTCyLKOqaguYDmvfgNzE4bIYdpWv4UT5EezuMkQl9B877PT/DQC7cLwx8LR3hQAAAABJRU5ErkJggg==)
+          no-repeat;
+        padding-left: 40px;
+      }
+      .browser .browser-firefox {
+        background-position: 0 -34px;
+      }
+      .browser .browser-ie {
+        background-position: 0 -68px;
+        margin-left: 0px;
+      }
+      .browser .browser-360 {
+        background-position: 0 -170px;
+        margin-left: -27px;
+      }
     </style>
   </head>
-  <body style="margin-top:50px">
+  <body style="margin-top: 50px">
     <h1>璇峰崌绾ф偍鐨勬祻瑙堝櫒锛屼互渚挎垜浠洿濂界殑涓烘偍鎻愪緵鏈嶅姟锛�</h1>
     <p>鎮ㄦ鍦ㄤ娇鐢� Internet Explorer 鐨勬棭鏈熺増鏈紙IE11浠ヤ笅鐗堟湰鎴栦娇鐢ㄨ鍐呮牳鐨勬祻瑙堝櫒锛夈�傝繖鎰忓懗鐫�鍦ㄥ崌绾ф祻瑙堝櫒鍓嶏紝鎮ㄥ皢鏃犳硶璁块棶姝ょ綉绔欍��</p>
     <hr />
diff --git a/index.html b/index.html
index c0dab51..aa1c86d 100644
--- a/index.html
+++ b/index.html
@@ -1,4 +1,4 @@
-<!DOCTYPE html>
+<!doctype html>
 <html>
   <head>
     <meta charset="utf-8" />
@@ -9,7 +9,7 @@
     <title>RuoYi-Vue-Plus澶氱鎴风鐞嗙郴缁�</title>
     <!--[if lt IE 11
       ]><script>
-        window.location.href='/html/ie.html';
+        window.location.href = '/html/ie.html';
       </script><!
     [endif]-->
     <style>
@@ -47,7 +47,7 @@
         margin: -75px 0 0 -75px;
         border-radius: 50%;
         border: 3px solid transparent;
-        border-top-color: #FFF;
+        border-top-color: #fff;
         -webkit-animation: spin 2s linear infinite;
         -ms-animation: spin 2s linear infinite;
         -moz-animation: spin 2s linear infinite;
@@ -57,7 +57,7 @@
       }
 
       #loader:before {
-        content: "";
+        content: '';
         position: absolute;
         top: 5px;
         left: 5px;
@@ -65,7 +65,7 @@
         bottom: 5px;
         border-radius: 50%;
         border: 3px solid transparent;
-        border-top-color: #FFF;
+        border-top-color: #fff;
         -webkit-animation: spin 3s linear infinite;
         -moz-animation: spin 3s linear infinite;
         -o-animation: spin 3s linear infinite;
@@ -74,7 +74,7 @@
       }
 
       #loader:after {
-        content: "";
+        content: '';
         position: absolute;
         top: 15px;
         left: 15px;
@@ -82,14 +82,13 @@
         bottom: 15px;
         border-radius: 50%;
         border: 3px solid transparent;
-        border-top-color: #FFF;
+        border-top-color: #fff;
         -moz-animation: spin 1.5s linear infinite;
         -o-animation: spin 1.5s linear infinite;
         -ms-animation: spin 1.5s linear infinite;
         -webkit-animation: spin 1.5s linear infinite;
         animation: spin 1.5s linear infinite;
       }
-
 
       @-webkit-keyframes spin {
         0% {
@@ -119,13 +118,12 @@
         }
       }
 
-
       #loader-wrapper .loader-section {
         position: fixed;
         top: 0;
         width: 51%;
         height: 100%;
-        background: #7171C6;
+        background: #7171c6;
         z-index: 1000;
         -webkit-transform: translateX(0);
         -ms-transform: translateX(0);
@@ -140,21 +138,20 @@
         right: 0;
       }
 
-
       .loaded #loader-wrapper .loader-section.section-left {
         -webkit-transform: translateX(-100%);
         -ms-transform: translateX(-100%);
         transform: translateX(-100%);
-        -webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
-        transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
+        -webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
+        transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
       }
 
       .loaded #loader-wrapper .loader-section.section-right {
         -webkit-transform: translateX(100%);
         -ms-transform: translateX(100%);
         transform: translateX(100%);
-        -webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
-        transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1.000);
+        -webkit-transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
+        transition: all 0.7s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
       }
 
       .loaded #loader {
@@ -182,7 +179,7 @@
 
       #loader-wrapper .load_title {
         font-family: 'Open Sans';
-        color: #FFF;
+        color: #fff;
         font-size: 19px;
         width: 100%;
         text-align: center;
@@ -197,7 +194,7 @@
         font-weight: normal;
         font-style: italic;
         font-size: 13px;
-        color: #FFF;
+        color: #fff;
         opacity: 0.5;
       }
     </style>
diff --git a/package.json b/package.json
index 6af6ba2..5154ed0 100644
--- a/package.json
+++ b/package.json
@@ -1,15 +1,16 @@
 {
   "name": "ruoyi-vue-plus",
-  "version": "5.1.2",
+  "version": "5.2.0-BETA",
   "description": "RuoYi-Vue-Plus澶氱鎴风鐞嗙郴缁�",
   "author": "LionLi",
   "license": "MIT",
+  "type": "module",
   "scripts": {
     "dev": "vite serve --mode development",
-    "build:prod": "vite build --mode production &&vue-tsc --noEmit",
+    "build:prod": "vite build --mode production",
+    "build:dev": "vite build --mode development",
     "preview": "vite preview",
-    "lint": "eslint src/**/*.{ts,js,vue} --fix",
-    "prepare": "husky install",
+    "lint:eslint": "eslint  --fix --ext .ts,.js,.vue ./src ",
     "prettier": "prettier --write ."
   },
   "repository": {
@@ -17,68 +18,84 @@
     "url": "https://gitee.com/JavaLionLi/plus-ui.git"
   },
   "dependencies": {
-    "@element-plus/icons-vue": "2.1.0",
+    "@element-plus/icons-vue": "2.3.1",
+    "@highlightjs/vue-plugin": "2.1.0",
+    "@lezer/common": "1.2.1",
     "@vueup/vue-quill": "1.2.0",
-    "@vueuse/core": "9.5.0",
+    "@vueuse/core": "10.9.0",
     "animate.css": "4.1.1",
-    "await-to-js": "^3.0.0",
-    "axios": "^1.3.4",
-    "echarts": "5.4.0",
-    "element-plus": "2.2.27",
+    "await-to-js": "3.0.0",
+    "axios": "1.6.8",
+    "bpmn-js": "16.4.0",
+    "camunda-bpmn-js-behaviors": "1.2.2",
+    "camunda-bpmn-moddle": "7.0.1",
+    "crypto-js": "4.2.0",
+    "diagram-js": "12.3.0",
+    "didi": "9.0.2",
+    "echarts": "5.5.0",
+    "element-plus": "2.7.2",
     "file-saver": "2.0.5",
-    "fuse.js": "6.6.2",
-    "js-cookie": "3.0.1",
-    "jsencrypt": "3.3.1",
-    "crypto-js": "^4.1.1",
+    "fuse.js": "7.0.0",
+    "highlight.js": "11.9.0",
+    "image-conversion": "^2.1.1",
+    "js-cookie": "3.0.5",
+    "jsencrypt": "3.3.2",
+    "moddle": "6.2.3",
     "nprogress": "0.2.0",
     "path-browserify": "1.0.1",
-    "path-to-regexp": "6.2.0",
-    "pinia": "2.0.22",
-    "screenfull": "6.0.0",
-    "vform3-builds": "3.0.8",
-    "vue": "3.2.45",
-    "vue-cropper": "1.0.3",
-    "vue-i18n": "9.2.2",
-    "vue-router": "4.1.4",
-    "vue-types": "^5.0.3"
+    "path-to-regexp": "6.2.1",
+    "pinia": "2.1.7",
+    "preact": "10.19.7",
+    "screenfull": "6.0.2",
+    "vform3-builds": "3.0.10",
+    "vue": "3.4.25",
+    "vue-cropper": "1.1.1",
+    "vue-i18n": "9.10.2",
+    "vue-router": "4.3.2",
+    "vue-types": "5.1.1",
+    "vxe-table": "4.5.22",
+    "zeebe-bpmn-moddle": "1.0.0"
   },
   "devDependencies": {
-    "@iconify/json": "^2.2.40",
-    "@intlify/unplugin-vue-i18n": "0.8.2",
-    "@types/crypto-js": "^4.1.1",
-    "@types/file-saver": "2.0.5",
-    "@types/js-cookie": "3.0.3",
-    "@types/node": "18.14.2",
-    "@types/nprogress": "0.2.0",
-    "@types/path-browserify": "^1.0.0",
-    "@typescript-eslint/eslint-plugin": "5.56.0",
-    "@typescript-eslint/parser": "5.56.0",
-    "@unocss/preset-attributify": "^0.50.6",
-    "@unocss/preset-icons": "^0.50.6",
-    "@unocss/preset-uno": "^0.50.6",
-    "@vitejs/plugin-vue": "4.0.0",
-    "@vue/compiler-sfc": "3.2.45",
-    "autoprefixer": "10.4.14",
-    "eslint": "8.36.0",
-    "eslint-config-prettier": "8.8.0",
-    "eslint-plugin-prettier": "4.2.1",
-    "eslint-plugin-vue": "9.9.0",
-    "fast-glob": "^3.2.11",
-    "husky": "7.0.4",
-    "postcss": "^8.4.21",
-    "prettier": "2.8.6",
-    "sass": "1.56.1",
-    "typescript": "4.9.5",
-    "unocss": "^0.50.6",
-    "unplugin-auto-import": "0.13.0",
-    "unplugin-icons": "0.15.1",
-    "unplugin-vue-components": "0.23.0",
-    "vite": "4.3.1",
+    "@iconify/json": "2.2.201",
+    "@intlify/unplugin-vue-i18n": "3.0.1",
+    "@types/crypto-js": "4.2.2",
+    "@types/file-saver": "2.0.7",
+    "@types/js-cookie": "3.0.6",
+    "@types/node": "18.18.2",
+    "@types/nprogress": "0.2.3",
+    "@types/path-browserify": "1.0.2",
+    "@typescript-eslint/eslint-plugin": "7.3.1",
+    "@typescript-eslint/parser": "7.3.1",
+    "@unocss/preset-attributify": "0.58.6",
+    "@unocss/preset-icons": "0.58.6",
+    "@unocss/preset-uno": "0.58.6",
+    "@vitejs/plugin-vue": "5.0.4",
+    "@vue/compiler-sfc": "3.4.23",
+    "autoprefixer": "10.4.18",
+    "eslint": "8.57.0",
+    "eslint-config-prettier": "9.1.0",
+    "eslint-define-config": "2.1.0",
+    "eslint-plugin-prettier": "5.1.3",
+    "eslint-plugin-promise": "6.1.1",
+    "eslint-plugin-node": "11.1.0",
+    "eslint-plugin-import": "2.29.1",
+    "eslint-plugin-vue": "9.23.0",
+    "fast-glob": "3.3.2",
+    "postcss": "8.4.36",
+    "prettier": "3.2.5",
+    "sass": "1.72.0",
+    "typescript": "5.4.5",
+    "unocss": "0.58.6",
+    "unplugin-auto-import": "0.17.5",
+    "unplugin-icons": "0.18.5",
+    "unplugin-vue-components": "0.26.0",
+    "unplugin-vue-setup-extend-plus": "1.0.1",
+    "vite": "5.2.10",
     "vite-plugin-compression": "0.5.1",
     "vite-plugin-svg-icons": "2.0.1",
-    "unplugin-vue-setup-extend-plus": "0.4.9",
-    "vitest": "^0.29.7",
-    "vue-eslint-parser": "9.1.0",
-    "vue-tsc": "0.35.0"
+    "vitest": "1.5.0",
+    "vue-eslint-parser": "9.4.2",
+    "vue-tsc": "2.0.13"
   }
 }
diff --git a/src/App.vue b/src/App.vue
index 41ffdd4..66dbed6 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -1,21 +1,20 @@
 <template>
-  <el-config-provider :locale="appStore.locale" :size="size">
+  <el-config-provider :locale="appStore.locale" :size="appStore.size">
     <router-view />
   </el-config-provider>
 </template>
 
 <script setup lang="ts">
-import useSettingsStore from '@/store/modules/settings'
-import { handleThemeStyle } from '@/utils/theme'
+import useSettingsStore from '@/store/modules/settings';
+import { handleThemeStyle } from '@/utils/theme';
 import useAppStore from '@/store/modules/app';
 
 const appStore = useAppStore();
-const size = computed(() => appStore.size as any);
 
 onMounted(() => {
   nextTick(() => {
     // 鍒濆鍖栦富棰樻牱寮�
-    handleThemeStyle(useSettingsStore().theme)
-  })
-})
+    handleThemeStyle(useSettingsStore().theme);
+  });
+});
 </script>
diff --git a/src/api/login.ts b/src/api/login.ts
index 100a5e9..b6955de 100644
--- a/src/api/login.ts
+++ b/src/api/login.ts
@@ -20,7 +20,8 @@
     url: '/auth/login',
     headers: {
       isToken: false,
-      isEncrypt: true
+      isEncrypt: true,
+      repeatSubmit: false
     },
     method: 'post',
     data: params
@@ -38,7 +39,8 @@
     url: '/auth/register',
     headers: {
       isToken: false,
-      isEncrypt: true
+      isEncrypt: true,
+      repeatSubmit: false
     },
     method: 'post',
     data: params
diff --git a/src/api/monitor/online/index.ts b/src/api/monitor/online/index.ts
index 3d9034a..7484702 100644
--- a/src/api/monitor/online/index.ts
+++ b/src/api/monitor/online/index.ts
@@ -18,3 +18,19 @@
     method: 'delete'
   });
 }
+
+// 鑾峰彇褰撳墠鐢ㄦ埛鐧诲綍鍦ㄧ嚎璁惧
+export function getOnline() {
+  return request({
+    url: '/monitor/online',
+    method: 'get'
+  });
+}
+
+// 鍒犻櫎褰撳墠鍦ㄧ嚎璁惧
+export function delOnline(tokenId: string) {
+  return request({
+    url: '/monitor/online/' + tokenId,
+    method: 'post'
+  });
+}
diff --git a/src/api/system/client/index.ts b/src/api/system/client/index.ts
index 06544da..6b302b0 100644
--- a/src/api/system/client/index.ts
+++ b/src/api/system/client/index.ts
@@ -64,12 +64,12 @@
 
 /**
  * 鐘舵�佷慨鏀�
- * @param id ID
+ * @param clientId 瀹㈡埛绔痠d
  * @param status 鐘舵��
  */
-export function changeStatus(id: number | string, status: string) {
+export function changeStatus(clientId: string, status: string) {
   const data = {
-    id,
+    clientId,
     status
   };
   return request({
diff --git a/src/api/system/client/types.ts b/src/api/system/client/types.ts
index d7cb926..142118d 100644
--- a/src/api/system/client/types.ts
+++ b/src/api/system/client/types.ts
@@ -7,7 +7,7 @@
   /**
    * 瀹㈡埛绔痠d
    */
-  clientId: string | number;
+  clientId: string;
 
   /**
    * 瀹㈡埛绔痥ey
diff --git a/src/api/system/config/index.ts b/src/api/system/config/index.ts
index e52a256..1e4842d 100644
--- a/src/api/system/config/index.ts
+++ b/src/api/system/config/index.ts
@@ -20,7 +20,7 @@
 }
 
 // 鏍规嵁鍙傛暟閿悕鏌ヨ鍙傛暟鍊�
-export function getConfigKey(configKey: string): AxiosPromise<String> {
+export function getConfigKey(configKey: string): AxiosPromise<string> {
   return request({
     url: '/system/config/configKey/' + configKey,
     method: 'get'
diff --git a/src/api/system/dept/types.ts b/src/api/system/dept/types.ts
index 867dbde..494745c 100644
--- a/src/api/system/dept/types.ts
+++ b/src/api/system/dept/types.ts
@@ -3,6 +3,7 @@
  */
 export interface DeptQuery extends PageQuery {
   deptName?: string;
+  deptCategory?: string;
   status?: number;
 }
 
@@ -16,6 +17,7 @@
   children: DeptVO[];
   deptId: number | string;
   deptName: string;
+  deptCategory: string;
   orderNum: number;
   leader: string;
   phone: string;
@@ -35,6 +37,7 @@
   children?: DeptForm[];
   deptId?: number | string;
   deptName?: string;
+  deptCategory?: string;
   orderNum?: number;
   leader?: string;
   phone?: string;
diff --git a/src/api/system/post/index.ts b/src/api/system/post/index.ts
index 3e523ab..5f6ab18 100644
--- a/src/api/system/post/index.ts
+++ b/src/api/system/post/index.ts
@@ -19,6 +19,18 @@
   });
 }
 
+// 鑾峰彇宀椾綅閫夋嫨妗嗗垪琛�
+export function optionselect(deptId?: number | string, postIds?: (number | string)[]): AxiosPromise<PostVO[]> {
+  return request({
+    url: '/system/post/optionselect',
+    method: 'get',
+    params: {
+      postIds: postIds,
+      deptId: deptId
+    }
+  });
+}
+
 // 鏂板宀椾綅
 export function addPost(data: PostForm) {
   return request({
diff --git a/src/api/system/post/types.ts b/src/api/system/post/types.ts
index 84e6786..45a0540 100644
--- a/src/api/system/post/types.ts
+++ b/src/api/system/post/types.ts
@@ -1,7 +1,10 @@
 export interface PostVO extends BaseEntity {
   postId: number | string;
+  deptId: number | string;
   postCode: string;
   postName: string;
+  postCategory: string;
+  deptName: string;
   postSort: number;
   status: string;
   remark: string;
@@ -9,15 +12,20 @@
 
 export interface PostForm {
   postId: number | string | undefined;
+  deptId: number | string | undefined;
   postCode: string;
   postName: string;
+  postCategory: string;
   postSort: number;
   status: string;
   remark: string;
 }
 
 export interface PostQuery extends PageQuery {
+  deptId: number | string;
+  belongDeptId: number | string;
   postCode: string;
   postName: string;
+  postCategory: string;
   status: string;
 }
diff --git a/src/api/system/role/index.ts b/src/api/system/role/index.ts
index 4e8b612..fb0fcab 100644
--- a/src/api/system/role/index.ts
+++ b/src/api/system/role/index.ts
@@ -13,6 +13,17 @@
 };
 
 /**
+ * 閫氳繃roleIds鏌ヨ瑙掕壊
+ * @param roleIds
+ */
+export const optionSelect = (roleIds: (number | string)[]): AxiosPromise<RoleVO[]> => {
+  return request({
+    url: '/system/role/optionselect?roleIds=' + roleIds,
+    method: 'get'
+  });
+};
+
+/**
  * 鏌ヨ瑙掕壊璇︾粏
  */
 export const getRole = (roleId: string | number): AxiosPromise<RoleVO> => {
@@ -142,3 +153,8 @@
     method: 'get'
   });
 };
+
+export default {
+  optionSelect,
+  listRole
+};
diff --git a/src/api/system/tenant/index.ts b/src/api/system/tenant/index.ts
index 4531a0a..4380dbe 100644
--- a/src/api/system/tenant/index.ts
+++ b/src/api/system/tenant/index.ts
@@ -25,7 +25,8 @@
     url: '/system/tenant',
     method: 'post',
     headers: {
-      isEncrypt: true
+      isEncrypt: true,
+      repeatSubmit: false
     },
     data: data
   });
diff --git a/src/api/system/user/index.ts b/src/api/system/user/index.ts
index fdcafc2..25c7884 100644
--- a/src/api/system/user/index.ts
+++ b/src/api/system/user/index.ts
@@ -18,6 +18,17 @@
 };
 
 /**
+ * 閫氳繃鐢ㄦ埛ids鏌ヨ鐢ㄦ埛
+ * @param userIds
+ */
+export const optionSelect = (userIds: (number | string)[]): AxiosPromise<UserVO[]> => {
+  return request({
+    url: '/system/user/optionselect?userIds=' + userIds,
+    method: 'get'
+  });
+};
+
+/**
  * 鑾峰彇鐢ㄦ埛璇︽儏
  * @param userId
  */
@@ -75,7 +86,8 @@
     url: '/system/user/resetPwd',
     method: 'put',
     headers: {
-      isEncrypt: true
+      isEncrypt: true,
+      repeatSubmit: false
     },
     data: data
   });
@@ -134,7 +146,8 @@
     url: '/system/user/profile/updatePwd',
     method: 'put',
     headers: {
-      isEncrypt: true
+      isEncrypt: true,
+      repeatSubmit: false
     },
     data: data
   });
@@ -199,6 +212,7 @@
 export default {
   listUser,
   getUser,
+  optionSelect,
   addUser,
   updateUser,
   delUser,
diff --git a/src/api/system/user/types.ts b/src/api/system/user/types.ts
index 77e9c32..3488e9f 100644
--- a/src/api/system/user/types.ts
+++ b/src/api/system/user/types.ts
@@ -1,4 +1,3 @@
-import { DeptVO } from './../dept/types';
 import { RoleVO } from '@/api/system/role/types';
 import { PostVO } from '@/api/system/post/types';
 
@@ -40,7 +39,7 @@
   loginIp: string;
   loginDate: string;
   remark: string;
-  dept: DeptVO;
+  deptName: string;
   roles: RoleVO[];
   roleIds: any;
   postIds: any;
diff --git a/src/api/tool/gen/index.ts b/src/api/tool/gen/index.ts
index b91df6a..efe079c 100644
--- a/src/api/tool/gen/index.ts
+++ b/src/api/tool/gen/index.ts
@@ -28,7 +28,7 @@
 };
 
 // 淇敼浠g爜鐢熸垚淇℃伅
-export const updateGenTable = (data: DbTableForm) => {
+export const updateGenTable = (data: DbTableForm): AxiosPromise<GenTableVO> => {
   return request({
     url: '/tool/gen',
     method: 'put',
@@ -37,7 +37,7 @@
 };
 
 // 瀵煎叆琛�
-export const importTable = (data: { tables: string; dataName: string }) => {
+export const importTable = (data: { tables: string; dataName: string }): AxiosPromise<GenTableVO> => {
   return request({
     url: '/tool/gen/importTable',
     method: 'post',
diff --git a/src/api/workflow/category/index.ts b/src/api/workflow/category/index.ts
new file mode 100644
index 0000000..e9723b0
--- /dev/null
+++ b/src/api/workflow/category/index.ts
@@ -0,0 +1,63 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { CategoryVO, CategoryForm, CategoryQuery } from '@/api/workflow/category/types';
+
+/**
+ * 鏌ヨ娴佺▼鍒嗙被鍒楄〃
+ * @param query
+ * @returns {*}
+ */
+
+export const listCategory = (query?: CategoryQuery): AxiosPromise<CategoryVO[]> => {
+  return request({
+    url: '/workflow/category/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 鏌ヨ娴佺▼鍒嗙被璇︾粏
+ * @param id
+ */
+export const getCategory = (id: string | number): AxiosPromise<CategoryVO> => {
+  return request({
+    url: '/workflow/category/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 鏂板娴佺▼鍒嗙被
+ * @param data
+ */
+export const addCategory = (data: CategoryForm) => {
+  return request({
+    url: '/workflow/category',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 淇敼娴佺▼鍒嗙被
+ * @param data
+ */
+export const updateCategory = (data: CategoryForm) => {
+  return request({
+    url: '/workflow/category',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 鍒犻櫎娴佺▼鍒嗙被
+ * @param id
+ */
+export const delCategory = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/workflow/category/' + id,
+    method: 'delete'
+  });
+};
diff --git a/src/api/workflow/category/types.ts b/src/api/workflow/category/types.ts
new file mode 100644
index 0000000..414fa55
--- /dev/null
+++ b/src/api/workflow/category/types.ts
@@ -0,0 +1,67 @@
+export interface CategoryVO {
+  /**
+   * 涓婚敭
+   */
+  id: string;
+
+  /**
+   * 鍒嗙被鍚嶇О
+   */
+  categoryName: string;
+
+  /**
+   * 鍒嗙被缂栫爜
+   */
+  categoryCode: string;
+
+  /**
+   * 鐖剁骇id
+   */
+  parentId: string | number;
+
+  /**
+   * 鎺掑簭
+   */
+  sortNum: number;
+
+  children?: CategoryVO[];
+}
+
+export interface CategoryForm extends BaseEntity {
+  /**
+   * 涓婚敭
+   */
+  id?: string | number;
+
+  /**
+   * 鍒嗙被鍚嶇О
+   */
+  categoryName?: string;
+
+  /**
+   * 鍒嗙被缂栫爜
+   */
+  categoryCode?: string;
+
+  /**
+   * 鐖剁骇id
+   */
+  parentId?: string | number;
+
+  /**
+   * 鎺掑簭
+   */
+  sortNum?: number;
+}
+
+export interface CategoryQuery extends PageQuery {
+  /**
+   * 鍒嗙被鍚嶇О
+   */
+  categoryName?: string;
+
+  /**
+   * 鍒嗙被缂栫爜
+   */
+  categoryCode?: string;
+}
diff --git a/src/api/workflow/definitionConfig/index.ts b/src/api/workflow/definitionConfig/index.ts
new file mode 100644
index 0000000..d34bf05
--- /dev/null
+++ b/src/api/workflow/definitionConfig/index.ts
@@ -0,0 +1,49 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { DefinitionConfigVO, DefinitionConfigForm } from '@/api/workflow/definitionConfig/types';
+
+/**
+ * 鏌ヨ琛ㄥ崟閰嶇疆璇︾粏
+ * @param definitionId
+ */
+export const getByDefId = (definitionId: string | number): AxiosPromise<DefinitionConfigVO> => {
+  return request({
+    url: '/workflow/definitionConfig/getByDefId/' + definitionId,
+    method: 'get'
+  });
+};
+
+/**
+ * 鏂板琛ㄥ崟閰嶇疆
+ * @param data
+ */
+export const saveOrUpdate = (data: DefinitionConfigForm) => {
+  return request({
+    url: '/workflow/definitionConfig/saveOrUpdate',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 鍒犻櫎琛ㄥ崟閰嶇疆
+ * @param id
+ */
+export const deldefinitionConfig = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/workflow/definitionConfig/' + id,
+    method: 'delete'
+  });
+};
+
+/**
+ * 鏌ヨ娴佺▼瀹氫箟閰嶇疆鎺掗櫎褰撳墠鏌ヨ鐨勬祦绋嬪畾涔�
+ * @param tableName
+ * @param definitionId
+ */
+export const getByTableNameNotDefId = (tableName: string, definitionId: string | number) => {
+  return request({
+    url: `/workflow/definitionConfig/getByTableNameNotDefId/${tableName}/${definitionId}`,
+    method: 'get'
+  });
+};
diff --git a/src/api/workflow/definitionConfig/types.ts b/src/api/workflow/definitionConfig/types.ts
new file mode 100644
index 0000000..7627403
--- /dev/null
+++ b/src/api/workflow/definitionConfig/types.ts
@@ -0,0 +1,102 @@
+import { FormManageVO } from '@/api/workflow/formManage/types';
+
+export interface DefinitionConfigVO {
+  /**
+   * 涓婚敭
+   */
+  id: string | number;
+
+  /**
+   * 琛ㄥ悕
+   */
+  tableName?: string;
+
+  /**
+   * 娴佺▼瀹氫箟ID
+   */
+  definitionId: string | number;
+
+  /**
+   * 娴佺▼KEY
+   */
+  processKey: string;
+
+  /**
+   * 娴佺▼鐗堟湰
+   */
+  version?: string | number;
+
+  /**
+   * 澶囨敞
+   */
+  remark: string;
+
+  /**
+   * 琛ㄥ崟绠$悊
+   */
+  wfFormManageVo: FormManageVO;
+}
+
+export interface DefinitionConfigForm extends BaseEntity {
+  /**
+   * 涓婚敭
+   */
+  id?: string | number;
+
+  /**
+   * 琛ㄥ悕
+   */
+  tableName?: string;
+
+  /**
+   * 娴佺▼瀹氫箟ID
+   */
+  definitionId?: string | number;
+
+  /**
+   * 娴佺▼KEY
+   */
+  processKey?: string;
+
+  /**
+   * 娴佺▼鐗堟湰
+   */
+  version?: string | number;
+
+  /**
+   * 澶囨敞
+   */
+  remark?: string;
+
+  /**
+   * 琛ㄥ崟绠$悊
+   */
+  wfFormManageVo?: FormManageVO;
+}
+
+export interface DefinitionConfigQuery extends PageQuery {
+  /**
+   * 琛ㄥ悕
+   */
+  tableName?: string;
+
+  /**
+   * 娴佺▼瀹氫箟ID
+   */
+  definitionId?: string | number;
+
+  /**
+   * 娴佺▼KEY
+   */
+  processKey?: string;
+
+  /**
+   * 娴佺▼鐗堟湰
+   */
+  version?: string | number;
+
+  /**
+   * 琛ㄥ崟绠$悊
+   */
+  wfFormManageVo: FormManageVO;
+}
diff --git a/src/api/workflow/formManage/index.ts b/src/api/workflow/formManage/index.ts
new file mode 100644
index 0000000..c2930cf
--- /dev/null
+++ b/src/api/workflow/formManage/index.ts
@@ -0,0 +1,76 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { FormManageVO, FormManageForm, FormManageQuery } from '@/api/workflow/formManage/types';
+
+/**
+ * 鏌ヨ琛ㄥ崟绠$悊鍒楄〃
+ * @param query
+ * @returns {*}
+ */
+
+export const listFormManage = (query?: FormManageQuery): AxiosPromise<FormManageVO[]> => {
+  return request({
+    url: '/workflow/formManage/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 鏌ヨ琛ㄥ崟绠$悊鍒楄〃
+ * @param query
+ * @returns {*}
+ */
+
+export const selectListFormManage = (): AxiosPromise<FormManageVO[]> => {
+  return request({
+    url: '/workflow/formManage/list/selectList',
+    method: 'get',
+  });
+};
+
+/**
+ * 鏌ヨ琛ㄥ崟绠$悊璇︾粏
+ * @param id
+ */
+export const getFormManage = (id: string | number): AxiosPromise<FormManageVO> => {
+  return request({
+    url: '/workflow/formManage/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 鏂板琛ㄥ崟绠$悊
+ * @param data
+ */
+export const addFormManage = (data: FormManageForm) => {
+  return request({
+    url: '/workflow/formManage',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 淇敼琛ㄥ崟绠$悊
+ * @param data
+ */
+export const updateFormManage = (data: FormManageForm) => {
+  return request({
+    url: '/workflow/formManage',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 鍒犻櫎琛ㄥ崟绠$悊
+ * @param id
+ */
+export const delFormManage = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/workflow/formManage/' + id,
+    method: 'delete'
+  });
+};
diff --git a/src/api/workflow/formManage/types.ts b/src/api/workflow/formManage/types.ts
new file mode 100644
index 0000000..6554f55
--- /dev/null
+++ b/src/api/workflow/formManage/types.ts
@@ -0,0 +1,69 @@
+export interface FormManageVO {
+  /**
+   * 涓婚敭
+   */
+  id: string | number;
+
+  /**
+   * 琛ㄥ崟鍚嶇О
+   */
+  formName: string;
+
+  /**
+   * 琛ㄥ崟绫诲瀷
+   */
+  formType: string;
+  /**
+   * 琛ㄥ崟绫诲瀷鍚嶇О
+   */
+  formTypeName: string;
+
+  /**
+   * 璺敱鍦板潃/琛ㄥ崟ID
+   */
+  router: string;
+
+  /**
+   * 澶囨敞
+   */
+  remork: string;
+}
+
+export interface FormManageForm extends BaseEntity {
+  /**
+   * 涓婚敭
+   */
+  id?: string | number;
+
+  /**
+   * 琛ㄥ崟鍚嶇О
+   */
+  formName?: string;
+
+  /**
+   * 琛ㄥ崟绫诲瀷
+   */
+  formType?: string;
+
+  /**
+   * 璺敱鍦板潃/琛ㄥ崟ID
+   */
+  router?: string;
+
+  /**
+   * 澶囨敞
+   */
+  remork?: string;
+}
+
+export interface FormManageQuery extends PageQuery {
+  /**
+   * 琛ㄥ崟鍚嶇О
+   */
+  formName?: string;
+
+  /**
+   * 琛ㄥ崟绫诲瀷
+   */
+  formType?: string;
+}
diff --git a/src/api/workflow/leave/index.ts b/src/api/workflow/leave/index.ts
new file mode 100644
index 0000000..36c6fdf
--- /dev/null
+++ b/src/api/workflow/leave/index.ts
@@ -0,0 +1,63 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { LeaveVO, LeaveQuery, LeaveForm } from '@/api/workflow/leave/types';
+
+/**
+ * 鏌ヨ璇峰亣鍒楄〃
+ * @param query
+ * @returns {*}
+ */
+
+export const listLeave = (query?: LeaveQuery): AxiosPromise<LeaveVO[]> => {
+  return request({
+    url: '/demo/leave/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 鏌ヨ璇峰亣璇︾粏
+ * @param id
+ */
+export const getLeave = (id: string | number): AxiosPromise<LeaveVO> => {
+  return request({
+    url: '/demo/leave/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 鏂板璇峰亣
+ * @param data
+ */
+export const addLeave = (data: LeaveForm): AxiosPromise<LeaveVO> => {
+  return request({
+    url: '/demo/leave',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 淇敼璇峰亣
+ * @param data
+ */
+export const updateLeave = (data: LeaveForm): AxiosPromise<LeaveVO> => {
+  return request({
+    url: '/demo/leave',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 鍒犻櫎璇峰亣
+ * @param id
+ */
+export const delLeave = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/demo/leave/' + id,
+    method: 'delete'
+  });
+};
diff --git a/src/api/workflow/leave/types.ts b/src/api/workflow/leave/types.ts
new file mode 100644
index 0000000..b093546
--- /dev/null
+++ b/src/api/workflow/leave/types.ts
@@ -0,0 +1,24 @@
+export interface LeaveVO {
+  id: string | number;
+  leaveType: string;
+  startDate: string;
+  endDate: string;
+  leaveDays: number;
+  remark: string;
+  processInstanceVo: any;
+}
+
+export interface LeaveForm extends BaseEntity {
+  id?: string | number;
+  leaveType?: string;
+  startDate?: string;
+  endDate?: string;
+  leaveDays?: number;
+  remark?: string;
+  processInstanceVo?: any;
+}
+
+export interface LeaveQuery extends PageQuery {
+  startLeaveDays?: number;
+  endLeaveDays?: number;
+}
diff --git a/src/api/workflow/model/index.ts b/src/api/workflow/model/index.ts
new file mode 100644
index 0000000..56f7fed
--- /dev/null
+++ b/src/api/workflow/model/index.ts
@@ -0,0 +1,104 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { ModelForm, ModelQuery, ModelVO } from '@/api/workflow/model/types';
+
+/**
+ * 鏌ヨ妯″瀷鍒楄〃
+ * @param query
+ * @returns {*}
+ */
+export const listModel = (query: ModelQuery): AxiosPromise<ModelVO[]> => {
+  return request({
+    url: '/workflow/model/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 鏌ヨ妯″瀷淇℃伅
+ * @param query
+ * @returns {*}
+ */
+export const getInfo = (id: string): AxiosPromise<ModelForm> => {
+  return request({
+    url: '/workflow/model/getInfo/'+id,
+    method: 'get'
+  });
+};
+
+/**
+ * 鏂板妯″瀷
+ * @param data
+ * @returns {*}
+ */
+export const addModel = (data: ModelForm): AxiosPromise<void> => {
+  return request({
+    url: '/workflow/model/save',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 淇敼妯″瀷淇℃伅
+ * @param data
+ * @returns {*}
+ */
+export function update(data: ModelForm): AxiosPromise<void> {
+  return request({
+    url: '/workflow/model/update',
+    method: 'put',
+    data: data
+  });
+}
+
+/**
+ * 淇敼妯″瀷淇℃伅
+ * @param data
+ * @returns {*}
+ */
+export function editModelXml(data: ModelForm): AxiosPromise<void> {
+  return request({
+    url: '/workflow/model/editModelXml',
+    method: 'put',
+    data: data
+  });
+}
+
+/**
+ * 鎸塱d鍒犻櫎妯″瀷
+ * @returns {*}
+ * @param id 妯″瀷id
+ */
+export function delModel(id: string | string[]): AxiosPromise<void> {
+  return request({
+    url: '/workflow/model/' + id,
+    method: 'delete'
+  });
+}
+
+/**
+ * 妯″瀷閮ㄧ讲
+ * @returns {*}
+ * @param id 妯″瀷id
+ */
+export const modelDeploy = (id: string): AxiosPromise<void> => {
+  return request({
+    url: `/workflow/model/modelDeploy/${id}`,
+    method: 'post'
+  });
+};
+
+/**
+ * 澶嶅埗妯″瀷
+ * @param data
+ * @returns {*}
+ */
+export const copyModel = (data: ModelForm): AxiosPromise<void> => {
+  return request({
+    url: '/workflow/model/copyModel',
+    method: 'post',
+    data: data
+  });
+};
\ No newline at end of file
diff --git a/src/api/workflow/model/types.ts b/src/api/workflow/model/types.ts
new file mode 100644
index 0000000..40a0faa
--- /dev/null
+++ b/src/api/workflow/model/types.ts
@@ -0,0 +1,66 @@
+export interface ModelForm {
+  id: string,
+  name: string;
+  key: string;
+  categoryCode: string;
+  xml:string,
+  svg:string,
+  description: string;
+}
+
+export interface ModelQuery extends PageQuery {
+  name?: string;
+  key?: string;
+  categoryCode?: string;
+}
+
+export interface OriginalPersistentState {
+  metaInfo: string;
+  editorSourceValueId: string;
+  createTime: string;
+  deploymentId?: string;
+  name: string;
+  tenantId: string;
+  category?: string;
+  version: number;
+  editorSourceExtraValueId?: string;
+  key: string;
+  lastUpdateTime: string;
+}
+
+export interface PersistentState {
+  metaInfo: string;
+  editorSourceValueId: string;
+  createTime: string;
+  deploymentId?: string;
+  name: string;
+  tenantId: string;
+  category?: string;
+  version: number;
+  editorSourceExtraValueId?: string;
+  key: string;
+  lastUpdateTime: string;
+}
+
+export interface ModelVO {
+  id: string;
+  revision: number;
+  originalPersistentState: OriginalPersistentState;
+  name: string;
+  key: string;
+  category?: string;
+  createTime: string;
+  lastUpdateTime: string;
+  version: number;
+  metaInfo: string;
+  deploymentId?: string;
+  editorSourceValueId: string;
+  editorSourceExtraValueId?: string;
+  tenantId: string;
+  persistentState: PersistentState;
+  revisionNext: number;
+  idPrefix: string;
+  inserted: boolean;
+  updated: boolean;
+  deleted: boolean;
+}
diff --git a/src/api/workflow/nodeConfig/index.ts b/src/api/workflow/nodeConfig/index.ts
new file mode 100644
index 0000000..3270c17
--- /dev/null
+++ b/src/api/workflow/nodeConfig/index.ts
@@ -0,0 +1,63 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { NodeConfigVO, NodeConfigForm, NodeConfigQuery } from '@/api/workflow/nodeConfig/types';
+
+/**
+ * 鏌ヨ鑺傜偣閰嶇疆鍒楄〃
+ * @param query
+ * @returns {*}
+ */
+
+export const listNodeConfig = (query?: NodeConfigQuery): AxiosPromise<NodeConfigVO[]> => {
+  return request({
+    url: '/workflow/nodeConfig/list',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 鏌ヨ鑺傜偣閰嶇疆璇︾粏
+ * @param id
+ */
+export const getNodeConfig = (id: string | number): AxiosPromise<NodeConfigVO> => {
+  return request({
+    url: '/workflow/nodeConfig/' + id,
+    method: 'get'
+  });
+};
+
+/**
+ * 鏂板鑺傜偣閰嶇疆
+ * @param data
+ */
+export const addNodeConfig = (data: NodeConfigForm) => {
+  return request({
+    url: '/workflow/nodeConfig',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 淇敼鑺傜偣閰嶇疆
+ * @param data
+ */
+export const updateNodeConfig = (data: NodeConfigForm) => {
+  return request({
+    url: '/workflow/nodeConfig',
+    method: 'put',
+    data: data
+  });
+};
+
+/**
+ * 鍒犻櫎鑺傜偣閰嶇疆
+ * @param id
+ */
+export const delNodeConfig = (id: string | number | Array<string | number>) => {
+  return request({
+    url: '/workflow/nodeConfig/' + id,
+    method: 'delete'
+  });
+};
diff --git a/src/api/workflow/nodeConfig/types.ts b/src/api/workflow/nodeConfig/types.ts
new file mode 100644
index 0000000..4e3a60b
--- /dev/null
+++ b/src/api/workflow/nodeConfig/types.ts
@@ -0,0 +1,43 @@
+import { FormManageVO } from '@/api/workflow/formManage/types';
+
+export interface NodeConfigVO {
+  /**
+   * 涓婚敭
+   */
+  id: string | number;
+
+  /**
+   * 琛ㄥ崟id
+   */
+  formId: string | number;
+
+  /**
+   * 琛ㄥ崟绫诲瀷
+   */
+  formType: string;
+
+  /**
+   * 鑺傜偣鍚嶇О
+   */
+  nodeName: string;
+
+  /**
+   * 鑺傜偣id
+   */
+  nodeId: string | number;
+
+  /**
+   * 娴佺▼瀹氫箟id
+   */
+  definitionId: string | number;
+
+  /**
+   * 琛ㄥ崟绠$悊
+   */
+  wfFormManageVo: FormManageVO;
+
+}
+
+
+
+
diff --git a/src/api/workflow/processDefinition/index.ts b/src/api/workflow/processDefinition/index.ts
new file mode 100644
index 0000000..c063120
--- /dev/null
+++ b/src/api/workflow/processDefinition/index.ts
@@ -0,0 +1,114 @@
+import request from '@/utils/request';
+import { ProcessDefinitionQuery, ProcessDefinitionVO, definitionXmlVO } from '@/api/workflow/processDefinition/types';
+import { AxiosPromise } from 'axios';
+
+/**
+ * 鑾峰彇娴佺▼瀹氫箟鍒楄〃
+ * @param query 娴佺▼瀹炰緥id
+ * @returns
+ */
+export const listProcessDefinition = (query: ProcessDefinitionQuery): AxiosPromise<ProcessDefinitionVO[]> => {
+  return request({
+    url: `/workflow/processDefinition/list`,
+    method: 'get',
+    params: query
+  });
+};
+/**
+ * 鎸夌収娴佺▼瀹氫箟key鑾峰彇娴佺▼瀹氫箟
+ * @param processInstanceId 娴佺▼瀹炰緥id
+ * @returns
+ */
+export const getListByKey = (key: string) => {
+  return request({
+    url: `/workflow/processDefinition/getListByKey/${key}`,
+    method: 'get'
+  });
+};
+
+/**
+ * 閫氳繃娴佺▼瀹氫箟id鑾峰彇娴佺▼鍥�
+ */
+export const definitionImage = (processDefinitionId: string): AxiosPromise<any> => {
+  return request({
+    url: `/workflow/processDefinition/definitionImage/${processDefinitionId}` + '?t' + Math.random(),
+    method: 'get'
+  });
+};
+
+/**
+ * 閫氳繃娴佺▼瀹氫箟id鑾峰彇xml
+ * @param processDefinitionId 娴佺▼瀹氫箟id
+ * @returns
+ */
+export const definitionXml = (processDefinitionId: string): AxiosPromise<definitionXmlVO> => {
+  return request({
+    url: `/workflow/processDefinition/definitionXml/${processDefinitionId}`,
+    method: 'get'
+  });
+};
+
+/**
+ * 鍒犻櫎娴佺▼瀹氫箟
+ * @param deploymentId 閮ㄧ讲id
+ * @param processDefinitionId 娴佺▼瀹氫箟id
+ * @returns
+ */
+export const deleteProcessDefinition = (deploymentId: string | string[], processDefinitionId: string | string[]) => {
+  return request({
+    url: `/workflow/processDefinition/${deploymentId}/${processDefinitionId}`,
+    method: 'delete'
+  });
+};
+
+/**
+ * 鎸傝捣/婵�娲�
+ * @param processDefinitionId 娴佺▼瀹氫箟id
+ * @returns
+ */
+export const updateDefinitionState = (processDefinitionId: string) => {
+  return request({
+    url: `/workflow/processDefinition/updateDefinitionState/${processDefinitionId}`,
+    method: 'put'
+  });
+};
+
+/**
+ * 娴佺▼瀹氫箟杞崲涓烘ā鍨�
+ * @param processDefinitionId 娴佺▼瀹氫箟id
+ * @returns
+ */
+export const convertToModel = (processDefinitionId: string) => {
+  return request({
+    url: `/workflow/processDefinition/convertToModel/${processDefinitionId}`,
+    method: 'put'
+  });
+};
+
+/**
+ * 閫氳繃zip鎴杧ml閮ㄧ讲娴佺▼瀹氫箟
+ * @returns
+ */
+export function deployProcessFile(data: any) {
+  return request({
+    url: '/workflow/processDefinition/deployByFile',
+    method: 'post',
+    data: data,
+    headers: {
+      repeatSubmit: false
+    }
+  });
+}
+
+/**
+ * 杩佺Щ娴佺▼
+ * @param currentProcessDefinitionId
+ * @param fromProcessDefinitionId
+ * @returns
+ */
+export const migrationDefinition = (currentProcessDefinitionId: string, fromProcessDefinitionId: string) => {
+  return request({
+    url: `/workflow/processDefinition/migrationDefinition/${currentProcessDefinitionId}/${fromProcessDefinitionId}`,
+    method: 'put'
+  });
+};
diff --git a/src/api/workflow/processDefinition/types.ts b/src/api/workflow/processDefinition/types.ts
new file mode 100644
index 0000000..979ec25
--- /dev/null
+++ b/src/api/workflow/processDefinition/types.ts
@@ -0,0 +1,24 @@
+import { DefinitionConfigVO } from '@/api/workflow/definitionConfig/types';
+export interface ProcessDefinitionQuery extends PageQuery {
+  key?: string;
+  name?: string;
+  categoryCode?: string;
+}
+
+export interface ProcessDefinitionVO extends BaseEntity {
+  id: string;
+  name: string;
+  key: string;
+  version: number;
+  suspensionState: number;
+  resourceName: string;
+  diagramResourceName: string;
+  deploymentId: string;
+  deploymentTime: string;
+  wfDefinitionConfigVo: DefinitionConfigVO;
+}
+
+export interface definitionXmlVO {
+  xml: string[];
+  xmlStr: string;
+}
diff --git a/src/api/workflow/processInstance/index.ts b/src/api/workflow/processInstance/index.ts
new file mode 100644
index 0000000..80f122f
--- /dev/null
+++ b/src/api/workflow/processInstance/index.ts
@@ -0,0 +1,136 @@
+import request from '@/utils/request';
+import { ProcessInstanceQuery, ProcessInstanceVO } from '@/api/workflow/processInstance/types';
+import { AxiosPromise } from 'axios';
+
+/**
+ * 鏌ヨ杩愯涓疄渚嬪垪琛�
+ * @param query
+ * @returns {*}
+ */
+export const getPageByRunning = (query: ProcessInstanceQuery): AxiosPromise<ProcessInstanceVO[]> => {
+  return request({
+    url: '/workflow/processInstance/getPageByRunning',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 鏌ヨ宸插畬鎴愬疄渚嬪垪琛�
+ * @param query
+ * @returns {*}
+ */
+export const getPageByFinish = (query: ProcessInstanceQuery): AxiosPromise<ProcessInstanceVO[]> => {
+  return request({
+    url: '/workflow/processInstance/getPageByFinish',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 閫氳繃娴佺▼瀹炰緥id鑾峰彇鍘嗗彶娴佺▼鍥�
+ */
+export const getHistoryImage = (processInstanceId: string) => {
+  return request({
+    url: `/workflow/processInstance/getHistoryImage/${processInstanceId}` + '?t' + Math.random(),
+    method: 'get'
+  });
+};
+
+/**
+ * 閫氳繃娴佺▼瀹炰緥id鑾峰彇鍘嗗彶娴佺▼鍥捐繍琛屼腑锛屽巻鍙茬瓑鑺傜偣
+ */
+export const getHistoryList = (instanceId: string): AxiosPromise<Record<string, any>> => {
+  return request({
+    url: `/workflow/processInstance/getHistoryList/${instanceId}` + '?t' + Math.random(),
+    method: 'get'
+  });
+};
+
+/**
+ * 鑾峰彇瀹℃壒璁板綍
+ * @param processInstanceId 娴佺▼瀹炰緥id
+ * @returns
+ */
+export const getHistoryRecord = (processInstanceId: string) => {
+  return request({
+    url: `/workflow/processInstance/getHistoryRecord/${processInstanceId}`,
+    method: 'get'
+  });
+};
+
+/**
+ * 浣滃簾
+ * @param data 鍙傛暟
+ * @returns
+ */
+export const deleteRunInstance = (data: object) => {
+  return request({
+    url: `/workflow/processInstance/deleteRunInstance`,
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 杩愯涓殑瀹炰緥 鍒犻櫎绋嬪疄渚嬶紝鍒犻櫎鍘嗗彶璁板綍锛屽垹闄や笟鍔′笌娴佺▼鍏宠仈淇℃伅
+ * @param processInstanceId 娴佺▼瀹炰緥id
+ * @returns
+ */
+export const deleteRunAndHisInstance = (processInstanceId: string | string[]) => {
+  return request({
+    url: `/workflow/processInstance/deleteRunAndHisInstance/${processInstanceId}`,
+    method: 'delete'
+  });
+};
+
+/**
+ * 宸插畬鎴愮殑瀹炰緥 鍒犻櫎绋嬪疄渚嬶紝鍒犻櫎鍘嗗彶璁板綍锛屽垹闄や笟鍔′笌娴佺▼鍏宠仈淇℃伅
+ * @param processInstanceId 娴佺▼瀹炰緥id
+ * @returns
+ */
+export const deleteFinishAndHisInstance = (processInstanceId: string | string[]) => {
+  return request({
+    url: `/workflow/processInstance/deleteFinishAndHisInstance/${processInstanceId}`,
+    method: 'delete'
+  });
+};
+
+/**
+ * 鍒嗛〉鏌ヨ褰撳墠鐧诲綍浜哄崟鎹�
+ * @param query
+ * @returns {*}
+ */
+export const getPageByCurrent = (query: ProcessInstanceQuery): AxiosPromise<ProcessInstanceVO[]> => {
+  return request({
+    url: '/workflow/processInstance/getPageByCurrent',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 鎾ら攢娴佺▼
+ * @param processInstanceId 娴佺▼瀹炰緥id
+ * @returns
+ */
+export const cancelProcessApply = (processInstanceId: string) => {
+  return request({
+    url: `/workflow/processInstance/cancelProcessApply/${processInstanceId}`,
+    method: 'post'
+  });
+};
+
+export default {
+  getPageByRunning,
+  getPageByFinish,
+  getHistoryImage,
+  getHistoryList,
+  getHistoryRecord,
+  deleteRunInstance,
+  deleteRunAndHisInstance,
+  deleteFinishAndHisInstance,
+  getPageByCurrent,
+  cancelProcessApply
+};
diff --git a/src/api/workflow/processInstance/types.ts b/src/api/workflow/processInstance/types.ts
new file mode 100644
index 0000000..99d0511
--- /dev/null
+++ b/src/api/workflow/processInstance/types.ts
@@ -0,0 +1,27 @@
+import { TaskVO } from '@/api/workflow/task/types';
+
+export interface ProcessInstanceQuery extends PageQuery {
+  categoryCode?: string;
+  name?: string;
+  key?: string;
+  startUserId?: string;
+  businessKey?: string;
+}
+
+export interface ProcessInstanceVO extends BaseEntity {
+  id: string;
+  processDefinitionId: string;
+  processDefinitionName: string;
+  processDefinitionKey: string;
+  processDefinitionVersion: string;
+  deploymentId: string;
+  businessKey: string;
+  isSuspended?: any;
+  tenantId: string;
+  startTime: string;
+  endTime?: string;
+  startUserId: string;
+  businessStatus: string;
+  businessStatusName: string;
+  taskVoList: TaskVO[];
+}
diff --git a/src/api/workflow/task/index.ts b/src/api/workflow/task/index.ts
new file mode 100644
index 0000000..d29de30
--- /dev/null
+++ b/src/api/workflow/task/index.ts
@@ -0,0 +1,264 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { TaskQuery, TaskVO } from '@/api/workflow/task/types';
+
+/**
+ * 鏌ヨ寰呭姙鍒楄〃
+ * @param query
+ * @returns {*}
+ */
+export const getPageByTaskWait = (query: TaskQuery): AxiosPromise<TaskVO[]> => {
+  return request({
+    url: '/workflow/task/getPageByTaskWait',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 鏌ヨ宸插姙鍒楄〃
+ * @param query
+ * @returns {*}
+ */
+export const getPageByTaskFinish = (query: TaskQuery): AxiosPromise<TaskVO[]> => {
+  return request({
+    url: '/workflow/task/getPageByTaskFinish',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 鏌ヨ褰撳墠鐢ㄦ埛鐨勬妱閫佸垪琛�
+ * @param query
+ * @returns {*}
+ */
+export const getPageByTaskCopy = (query: TaskQuery): AxiosPromise<TaskVO[]> => {
+  return request({
+    url: '/workflow/task/getPageByTaskCopy',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 褰撳墠绉熸埛鎵�鏈夊緟鍔炰换鍔�
+ * @param query
+ * @returns {*}
+ */
+export const getPageByAllTaskWait = (query: TaskQuery): AxiosPromise<TaskVO[]> => {
+  return request({
+    url: '/workflow/task/getPageByAllTaskWait',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 褰撳墠绉熸埛鎵�鏈夊凡鍔炰换鍔�
+ * @param query
+ * @returns {*}
+ */
+export const getPageByAllTaskFinish = (query: TaskQuery): AxiosPromise<TaskVO[]> => {
+  return request({
+    url: '/workflow/task/getPageByAllTaskFinish',
+    method: 'get',
+    params: query
+  });
+};
+
+/**
+ * 鍚姩娴佺▼
+ * @param data
+ * @returns {*}
+ */
+export const startWorkFlow = (data: object): any => {
+  return request({
+    url: '/workflow/task/startWorkFlow',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 鍔炵悊娴佺▼
+ * @param data
+ * @returns {*}
+ */
+export const completeTask = (data: object) => {
+  return request({
+    url: '/workflow/task/completeTask',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 璁ら浠诲姟
+ * @param taskId
+ * @returns {*}
+ */
+export const claim = (taskId: string): any => {
+  return request({
+    url: '/workflow/task/claim/' + taskId,
+    method: 'post'
+  });
+};
+
+/**
+ * 褰掕繕浠诲姟
+ * @param taskId
+ * @returns {*}
+ */
+export const returnTask = (taskId: string): any => {
+  return request({
+    url: '/workflow/task/returnTask/' + taskId,
+    method: 'post'
+  });
+};
+
+/**
+ * 浠诲姟椹冲洖
+ * @param data
+ * @returns {*}
+ */
+export const backProcess = (data: any): any => {
+  return request({
+    url: '/workflow/task/backProcess',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 鑾峰彇褰撳墠浠诲姟
+ * @param taskId
+ * @returns
+ */
+export const getTaskById = (taskId: string) => {
+  return request({
+    url: '/workflow/task/getTaskById/' + taskId,
+    method: 'get'
+  });
+};
+
+/**
+ * 鍔犵
+ * @param data
+ * @returns
+ */
+export const addMultiInstanceExecution = (data: any) => {
+  return request({
+    url: '/workflow/task/addMultiInstanceExecution',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 鍑忕
+ * @param data
+ * @returns
+ */
+export const deleteMultiInstanceExecution = (data: any) => {
+  return request({
+    url: '/workflow/task/deleteMultiInstanceExecution',
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 淇敼浠诲姟鍔炵悊浜�
+ * @param taskIds
+ * @param userId
+ * @returns
+ */
+export const updateAssignee = (taskIds: Array<string>, userId: string) => {
+  return request({
+    url: `/workflow/task/updateAssignee/${taskIds}/${userId}`,
+    method: 'put'
+  });
+};
+
+/**
+ * 杞姙浠诲姟
+ * @returns
+ */
+export const transferTask = (data: any) => {
+  return request({
+    url: `/workflow/task/transferTask`,
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 缁堟浠诲姟
+ * @returns
+ */
+export const terminationTask = (data: any) => {
+  return request({
+    url: `/workflow/task/terminationTask`,
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 鏌ヨ娴佺▼鍙橀噺
+ * @returns
+ */
+export const getInstanceVariable = (taskId: string) => {
+  return request({
+    url: `/workflow/task/getInstanceVariable/${taskId}`,
+    method: 'get'
+  });
+};
+
+/**
+ * 鑾峰彇鍙┏鍥炲緱浠诲姟鑺傜偣
+ * @returns
+ */
+export const getTaskNodeList = (processInstanceId: string) => {
+  return request({
+    url: `/workflow/task/getTaskNodeList/${processInstanceId}`,
+    method: 'get'
+  });
+};
+
+/**
+ * 濮旀墭浠诲姟
+ * @returns
+ */
+export const delegateTask = (data: any) => {
+  return request({
+    url: `/workflow/task/delegateTask`,
+    method: 'post',
+    data: data
+  });
+};
+
+/**
+ * 鏌ヨ宸ヤ綔娴佷换鍔$敤鎴烽�夋嫨鍔犵浜哄憳
+ * @param taskId
+ * @returns {*}
+ */
+export const getTaskUserIdsByAddMultiInstance = (taskId: string) => {
+  return request({
+    url: '/workflow/task/getTaskUserIdsByAddMultiInstance/' + taskId,
+    method: 'get'
+  });
+};
+
+/**
+ * 鏌ヨ宸ヤ綔娴侀�夋嫨鍑忕浜哄憳
+ * @param taskId
+ * @returns {*}
+ */
+export const getListByDeleteMultiInstance = (taskId: string) => {
+  return request({
+    url: '/workflow/task/getListByDeleteMultiInstance/' + taskId,
+    method: 'get'
+  });
+};
diff --git a/src/api/workflow/task/types.ts b/src/api/workflow/task/types.ts
new file mode 100644
index 0000000..0425a1a
--- /dev/null
+++ b/src/api/workflow/task/types.ts
@@ -0,0 +1,49 @@
+import { NodeConfigVO } from '@/api/workflow/nodeConfig/types';
+import { DefinitionConfigVO } from '@/api/workflow/definitionConfig/types';
+export interface TaskQuery extends PageQuery {
+  name?: string;
+  processDefinitionKey?: string;
+  processDefinitionName?: string;
+}
+
+export interface ParticipantVo {
+  groupIds?: string[] | number[];
+  candidate: string[] | number[];
+  candidateName: string[];
+  claim: boolean;
+}
+
+export interface TaskVO extends BaseEntity {
+  id: string;
+  name: string;
+  description?: string;
+  priority: number;
+  owner?: string;
+  assignee?: string | number;
+  assigneeName?: string;
+  processInstanceId: string;
+  executionId: string;
+  taskDefinitionId?: any;
+  processDefinitionId: string;
+  endTime?: string;
+  taskDefinitionKey: string;
+  dueDate?: string;
+  category?: any;
+  parentTaskId?: any;
+  tenantId: string;
+  claimTime?: string;
+  businessStatus?: string;
+  businessStatusName?: string;
+  processDefinitionName?: string;
+  processDefinitionKey?: string;
+  participantVo?: ParticipantVo;
+  multiInstance?: boolean;
+  businessKey?: string;
+  wfNodeConfigVo?: NodeConfigVO;
+  wfDefinitionConfigVo?: DefinitionConfigVO;
+}
+
+export interface VariableVo {
+  key: string;
+  value: string;
+}
diff --git a/src/api/workflow/workflowCommon/index.ts b/src/api/workflow/workflowCommon/index.ts
new file mode 100644
index 0000000..a90216a
--- /dev/null
+++ b/src/api/workflow/workflowCommon/index.ts
@@ -0,0 +1,29 @@
+import { RouterJumpVo } from '@/api/workflow/workflowCommon/types';
+
+export default {
+    routerJump(routerJumpVo: RouterJumpVo,proxy){
+        if (routerJumpVo.wfNodeConfigVo && routerJumpVo.wfNodeConfigVo.formType === 'static' && routerJumpVo.wfNodeConfigVo.wfFormManageVo) {
+            proxy.$tab.closePage(proxy.$route);
+            proxy.$router.push({
+                path: `${routerJumpVo.wfNodeConfigVo.wfFormManageVo.router}`,
+                query: {
+                    id: routerJumpVo.businessKey,
+                    type: routerJumpVo.type,
+                    taskId: routerJumpVo.taskId
+                }
+            });
+        } else if (routerJumpVo.wfNodeConfigVo && routerJumpVo.wfNodeConfigVo.formType === 'dynamic' && routerJumpVo.wfNodeConfigVo.wfFormManageVo) {
+            proxy.$tab.closePage(proxy.$route);
+            proxy.$router.push({
+                path: `${routerJumpVo.wfNodeConfigVo.wfFormManageVo.router}`,
+                query: {
+                    id: routerJumpVo.businessKey,
+                    type: routerJumpVo.type,
+                    taskId: routerJumpVo.taskId
+                }
+            });
+        }else {
+            proxy?.$modal.msgError('璇峰埌妯″瀷閰嶇疆鑿滃崟锛�');
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/api/workflow/workflowCommon/types.ts b/src/api/workflow/workflowCommon/types.ts
new file mode 100644
index 0000000..0f1ef1f
--- /dev/null
+++ b/src/api/workflow/workflowCommon/types.ts
@@ -0,0 +1,16 @@
+import { NodeConfigVO } from '@/api/workflow/nodeConfig/types';
+import { DefinitionConfigVO } from '@/api/workflow/definitionConfig/types';
+
+export interface RouterJumpVo {
+  wfNodeConfigVo: NodeConfigVO;
+  wfDefinitionConfigVo: DefinitionConfigVO;
+  businessKey: string;
+  taskId: string;
+  type: string;
+}
+
+export interface StartProcessBo {
+  businessKey: string | number;
+  tableName: string;
+  variables: any;
+}
diff --git a/src/assets/icons/svg/caret-back.svg b/src/assets/icons/svg/caret-back.svg
new file mode 100644
index 0000000..9bae722
--- /dev/null
+++ b/src/assets/icons/svg/caret-back.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" class="ionicon" viewBox="0 0 512 512"><path d="M321.94 98L158.82 237.78a24 24 0 000 36.44L321.94 414c15.57 13.34 39.62 2.28 39.62-18.22v-279.6c0-20.5-24.05-31.56-39.62-18.18z"/></svg>
\ No newline at end of file
diff --git a/src/assets/icons/svg/caret-forward.svg b/src/assets/icons/svg/caret-forward.svg
new file mode 100644
index 0000000..1ec3f7d
--- /dev/null
+++ b/src/assets/icons/svg/caret-forward.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" class="ionicon" viewBox="0 0 512 512"><path d="M190.06 414l163.12-139.78a24 24 0 000-36.44L190.06 98c-15.57-13.34-39.62-2.28-39.62 18.22v279.6c0 20.5 24.05 31.56 39.62 18.18z"/></svg>
\ No newline at end of file
diff --git a/src/assets/icons/svg/category.svg b/src/assets/icons/svg/category.svg
new file mode 100644
index 0000000..df92526
--- /dev/null
+++ b/src/assets/icons/svg/category.svg
@@ -0,0 +1 @@
+<svg t="1715954426124" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3305" width="200" height="200"><path d="M664.081597 1023.943114a78.246037 78.246037 0 0 1-78.985549-76.795456v-284.996471a78.27448 78.27448 0 0 1 78.985549-76.93767h280.843828A78.189152 78.189152 0 0 1 1023.939417 662.151187v284.996471a78.246037 78.246037 0 0 1-79.013992 76.795456z m-585.067605 0a78.246037 78.246037 0 0 1-78.985549-76.795456v-284.996471a78.160709 78.160709 0 0 1 78.985549-76.93767h280.786942a78.302923 78.302923 0 0 1 79.042434 76.93767v284.996471h-0.170656a78.246037 78.246037 0 0 1-78.985549 76.795456z m0-585.096048a78.217594 78.217594 0 0 1-78.985549-76.93767V76.912925a78.189152 78.189152 0 0 1 78.957106-76.795456h280.786942a78.27448 78.27448 0 0 1 79.042435 76.93767v284.996471a78.27448 78.27448 0 0 1-79.013992 76.795456z m589.675333-5.688552a77.193655 77.193655 0 0 1-77.990052-75.885288V75.888985a77.25054 77.25054 0 0 1 77.990052-75.942173h277.26004a77.25054 77.25054 0 0 1 77.961609 75.942173v281.384241a77.421197 77.421197 0 0 1-78.132266 75.885288z" p-id="3306" fill="currentColor"></path></svg>
\ No newline at end of file
diff --git a/src/assets/icons/svg/finish.svg b/src/assets/icons/svg/finish.svg
new file mode 100644
index 0000000..4685c23
--- /dev/null
+++ b/src/assets/icons/svg/finish.svg
@@ -0,0 +1 @@
+<svg t="1716006237008" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="12400" width="200" height="200"><path d="M738.826039 1005.166431c-150.226824 0-272.00251-121.916235-272.00251-272.303686 0-150.407529 121.775686-272.323765 272.00251-272.323765 150.206745 0 271.982431 121.916235 271.982432 272.323765 0 150.387451-121.775686 272.303686-271.982432 272.303686z m-0.040157-508.225255c-128.582275 0-232.789333 104.347608-232.789333 233.09051s104.207059 233.110588 232.789333 233.110589c128.562196 0 232.769255-104.367686 232.769255-233.110589 0-128.742902-104.207059-233.09051-232.769255-233.09051z m10.561255 318.243138s-3.694431 3.674353-7.408941 3.674353a18.010353 18.010353 0 0 1-25.941333 0l-74.10949-80.916079a17.66902 17.66902 0 0 1 0-25.740549c7.408941-7.368784 22.246902-7.368784 25.941333 0l63.006118 69.872941 129.686588-117.699764a18.010353 18.010353 0 0 1 25.941333 0 17.709176 17.709176 0 0 1 0 25.760627L749.347137 815.184314zM391.529412 682.666667H190.745098a20.078431 20.078431 0 0 1 0-40.156863h200.784314a20.078431 20.078431 0 1 1 0 40.156863zM170.666667 261.019608a20.078431 20.078431 0 0 1 20.078431-20.078432h481.882353a20.078431 20.078431 0 0 1 0 40.156863H190.745098a20.078431 20.078431 0 0 1-20.078431-20.078431z m341.333333 200.784314H190.745098a20.078431 20.078431 0 0 1 0-40.156863h321.254902a20.078431 20.078431 0 0 1 0 40.156863zM813.176471 120.470588a80.313725 80.313725 0 0 0-80.313726-80.313725H130.509804a80.313725 80.313725 0 0 0-80.313726 80.313725v762.980392a80.313725 80.313725 0 0 0 80.313726 80.313726h366.832941a346.112 346.112 0 0 0 40.417882 40.779294H130.509804a120.470588 120.470588 0 0 1-120.470588-120.470588V120.470588a120.470588 120.470588 0 0 1 120.470588-120.470588h602.352941a120.470588 120.470588 0 0 1 120.470588 120.470588v293.667137a340.188863 340.188863 0 0 0-40.156862-8.533333V120.470588z" fill="currentColor" p-id="12401"></path></svg>
\ No newline at end of file
diff --git a/src/assets/icons/svg/model.svg b/src/assets/icons/svg/model.svg
new file mode 100644
index 0000000..0c57d70
--- /dev/null
+++ b/src/assets/icons/svg/model.svg
@@ -0,0 +1 @@
+<svg t="1715953291934" class="icon" viewBox="0 0 1061 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1715" id="mx_n_1715953291935" width="200" height="200"><path d="M447.122465 467.332105L49.240301 268.161564A33.501036 33.501036 0 0 0 0.136043 300.744763v441.020484a33.042117 33.042117 0 0 0 16.06214 27.994016L413.162511 1018.034062a33.959954 33.959954 0 0 0 17.438895 5.50702 33.042117 33.042117 0 0 0 33.042117-33.042118V497.161795a33.042117 33.042117 0 0 0-17.438895-29.82969zM398.018207 931.298504l-331.339011-208.348907v-367.134638l331.339011 162.915996zM1046.010843 263.572381a33.042117 33.042117 0 0 0-31.665363 0L550.838 467.332105a33.042117 33.042117 0 0 0-19.733487 30.288608v493.33717a33.042117 33.042117 0 0 0 49.563176 28.452934l463.048562-265.254776a33.042117 33.042117 0 0 0 16.521059-28.452934V291.566398a33.042117 33.042117 0 0 0-14.685386-27.994017z m-50.939931 441.020484L596.72983 931.298504v-413.026468l397.882163-176.224626zM991.399565 178.672496a33.042117 33.042117 0 0 0-22.486996-29.829689L550.838 1.530034a32.583199 32.583199 0 0 0-19.733487 0L83.659173 158.021173a33.042117 33.042117 0 0 0-4.130264 61.036134l397.882163 199.170541a33.042117 33.042117 0 0 0 14.685386 3.212428 33.959954 33.959954 0 0 0 13.30863 0l463.966399-205.595398a33.042117 33.042117 0 0 0 22.028078-37.172382zM494.391049 349.849021L180.490934 195.193555l358.874108-125.743613 328.126583 112.434982z m0 0" fill="currentColor" p-id="1716"></path></svg>
\ No newline at end of file
diff --git a/src/assets/icons/svg/my-copy.svg b/src/assets/icons/svg/my-copy.svg
new file mode 100644
index 0000000..49f69fa
--- /dev/null
+++ b/src/assets/icons/svg/my-copy.svg
@@ -0,0 +1 @@
+<svg t="1716006583362" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="38505" width="200" height="200"><path d="M733.696 666.624l56.32-65.536-15.36-12.8c-41.472-34.816-87.552-61.44-136.192-79.36 75.264-49.152 124.928-134.144 124.928-230.912 0-152.576-123.904-276.48-276.48-276.48-74.24 0-143.872 28.672-195.584 80.384-52.224 51.712-80.896 121.344-80.896 195.584 0 92.16 45.568 174.08 115.2 224.256-81.408 26.624-156.672 74.752-215.552 144.896C34.304 736.768-4.096 850.944 0.512 968.192l1.024 20.48 86.528-4.608-1.024-19.968c-4.096-96.256 27.136-188.928 88.576-261.12 136.704-162.816 380.416-184.32 543.232-48.64l14.848 12.288zM296.96 278.016c0-106.496 83.456-189.952 189.952-189.952 104.96 0 189.952 84.992 189.952 189.952 0 106.496-83.456 189.952-189.952 189.952S296.96 384.512 296.96 278.016z m690.688 522.24H802.304c13.824-16.896 32.256-38.4 55.808-67.072 7.68-8.192 11.776-19.456 10.752-31.744-1.024-11.776-6.144-22.528-15.36-29.696-8.192-7.68-19.456-11.264-31.232-10.752-12.288 1.024-23.04 6.656-30.208 15.872-38.4 45.568-96.256 114.176-101.376 119.808-7.68 7.68-10.752 15.36-13.312 22.528-4.096 8.704-4.096 16.384-4.096 24.064 0 5.632 0 12.8 3.584 23.04 2.56 7.68 6.144 15.872 13.824 23.552l104.96 124.416 4.096 2.048c9.216 4.096 18.432 6.144 26.624 6.144 8.704 0 21.504-4.096 28.672-11.776 8.704-8.704 13.824-19.968 14.336-31.744 0-10.752-3.584-20.48-11.264-28.16l-54.272-63.488h183.296c19.456 0 35.84-18.944 36.352-43.008v-0.512c0.512-25.088-14.848-43.52-35.84-43.52z" fill="currentColor" p-id="38506"></path></svg>
\ No newline at end of file
diff --git a/src/assets/icons/svg/my-task.svg b/src/assets/icons/svg/my-task.svg
new file mode 100644
index 0000000..1f1ea44
--- /dev/null
+++ b/src/assets/icons/svg/my-task.svg
@@ -0,0 +1 @@
+<svg t="1715953932254" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="11266" width="200" height="200"><path d="M955.59576334 565.84816928C921.71190561 470.40769794 828.48968821 401.08719229 717.79871055 401.08719229c-111.30100029 0-204.91141024 70.09689085-238.29616047 166.36920831h-253.43576475c-26.23088067 0-47.5261393 24.84446618-47.5261329 55.45640711s21.2952586 55.4564006 47.5261329 55.45640054h242.73267482c6.54385326 41.3150179 23.95716606 79.02537397 49.30074631 110.91280755h-292.03342113c-26.23088067 0-47.5261393 24.84446618-47.5261329 55.45640061s21.2952586 55.4564006 47.5261329 55.45640059h214.0617167a376.93717152 376.93717152 0 0 0-33.44021086 110.91280754h-292.47706801a46.36155474 46.36155474 0 0 1-45.91790151-46.8052016V169.1685161c0-25.06629616 21.23980441-45.36333956 47.35977009-45.3633331h63.38666838v55.45640059c0 50.07713168 67.6013528 55.4564006 110.91280122 55.45640061 43.31144833 0 88.50841878 5.76746809 88.5084187-55.45640061v-55.45640059h44.53149358v55.45640059c0 61.22386864 38.15400297 55.4564006 88.84115713 55.45640061 50.74260854 0 88.56387296 5.76746809 88.56387308-55.45640061v-55.45640059h44.19875511v55.45640059c0 61.22386864 38.43128719 55.4564006 89.11844137 55.45640061s110.91280771-2.82827659 110.91280118-55.45640061v-55.45640059h63.44212904a47.69250853 47.69250853 0 0 1 47.74796275 47.63704778l-0.22182994 394.4059385zM407.96379074 345.63079175h-181.95245955c-26.23088067 0-47.5261393 24.84446618-47.52613946 55.45640054s21.2952586 55.4564006 47.52613946 55.45640711h181.95245955c26.23088067 0 47.5261393-24.84446618 47.52613268-55.45640711s-21.2952586-55.4564006-47.52613268-55.45640054z m325.75090957-166.36920816c-30.61193437 0-55.4564006-18.63335139-55.45640712-41.59230208v-83.18460405c0-22.95895071 24.84446618-41.59230206 55.45640712-41.592302s55.4564006 18.63335139 55.4564006 41.592302v83.18460405c0 22.95895071-24.84446618 41.59230206-55.4564006 41.59230208z m-222.71291552 0c-30.61193437 0-55.4009463-18.63335139-55.4009464-41.59230208v-83.18460405c0-22.95895071 24.84446618-41.59230206 55.4009464-41.592302 30.61193437 0 55.4564006 18.63335139 55.45640054 41.592302v83.18460405c-0.0554542 22.95895071-24.84446618 41.59230206-55.45640054 41.59230208z m-220.6610244 0c-30.61193437 0-55.4009463-18.63335139-55.40094634-41.59230208v-83.18460405c0-22.95895071 24.84446618-41.59230206 55.40094634-41.592302 30.61193437 0 55.4564006 18.63335139 55.45640063 41.592302v83.18460405c0 22.95895071-24.84446618 41.59230206-55.45640063 41.59230208z m443.31847922 665.92048622c-91.28124099 0-165.53736215-74.69977454-165.53736218-166.59103817 0-91.83580289 74.2561211-166.59103164 165.53736218-166.59103164s165.53736215 74.75522879 165.53736218 166.59103164-74.2561211 166.59103164-165.53736218 166.59103817z m-115.73750824-29.33644132c32.1647114 24.45627358 71.98241282 39.59587158 115.68205395 39.59587144 44.14329437 0 84.34918863-15.47233629 116.68026924-40.3722568a235.13514545 235.13514545 0 0 1 105.14533952 195.98292729h-443.6512175c0-80.35632762 41.92504052-153.94697186 106.14355479-195.20654193z" fill="currentColor" p-id="11267"></path></svg>
\ No newline at end of file
diff --git a/src/assets/icons/svg/process-definition.svg b/src/assets/icons/svg/process-definition.svg
new file mode 100644
index 0000000..202d200
--- /dev/null
+++ b/src/assets/icons/svg/process-definition.svg
@@ -0,0 +1 @@
+<svg t="1716005059256" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3268" width="200" height="200"><path d="M497.798958 952.225272A345.95419 345.95419 0 0 1 359.273733 892.652247a171.541601 171.541601 0 0 0 7.177473-47.37132A179.436821 179.436821 0 0 0 211.417793 665.126359a345.95419 345.95419 0 0 1 185.896546-380.40606 179.436821 179.436821 0 0 0 317.244299 0 351.696169 351.696169 0 0 1 143.549456 128.476763 35.169617 35.169617 0 0 0 30.145386 16.508188 37.322859 37.322859 0 0 0 19.379177-5.024231 36.605111 36.605111 0 0 0 10.766209-46.653574 424.188644 424.188644 0 0 0-183.743304-160.775391v-13.637198a180.872315 180.872315 0 1 0-358.873642 0 129.194511 129.194511 0 0 0 0 15.79044A423.470897 423.470897 0 0 0 132.465591 600.529103a467.253481 467.253481 0 0 0 6.459726 71.774728A180.154568 180.154568 0 0 0 187.014385 1024a178.001326 178.001326 0 0 0 139.96072-68.185992 430.64837 430.64837 0 0 0 158.62215 62.444014h6.459725a35.887364 35.887364 0 0 0 5.741978-71.774729z m57.419783-861.29674a109.097587 109.097587 0 1 1-108.37984 110.533082A109.097587 109.097587 0 0 1 555.218741 90.928532zM187.014385 952.225272a109.097587 109.097587 0 1 1 108.37984-108.37984A109.097587 109.097587 0 0 1 187.014385 952.225272zM933.471559 617.755038l-104.791103-71.774728a35.887364 35.887364 0 0 0-48.089068 8.612967L560.242972 858.918125a37.322859 37.322859 0 0 0-6.459725 24.403408l9.330714 104.073356a35.169617 35.169617 0 0 0 14.354946 25.838902 38.758353 38.758353 0 0 0 21.532418 7.177473h7.177473l98.331378-21.532419a38.758353 38.758353 0 0 0 22.250166-14.354946L945.673263 665.126359a36.605111 36.605111 0 0 0 5.741978-27.274397 37.322859 37.322859 0 0 0-17.943682-20.096924zM675.800285 930.692853l-45.218079 9.330715-4.306484-49.524563 193.791766-262.695505 45.218079 29.427638z m311.50232-399.067489l-103.355608-66.750497a35.887364 35.887364 0 0 0-49.524563 10.048462 35.169617 35.169617 0 0 0 10.766209 49.524562L947.826505 593.35163a34.45187 34.45187 0 0 0 20.096924 5.741979 37.322859 37.322859 0 0 0 30.145386-15.790441 36.605111 36.605111 0 0 0-10.76621-51.677804z" fill="currentColor" p-id="3269"></path></svg>
\ No newline at end of file
diff --git a/src/assets/icons/svg/topiam.svg b/src/assets/icons/svg/topiam.svg
new file mode 100644
index 0000000..e7ea057
--- /dev/null
+++ b/src/assets/icons/svg/topiam.svg
@@ -0,0 +1,29 @@
+<svg width="200" height="200" viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_446_540)">
+<path d="M113.069 160.072C103.717 170.743 93.0453 180.216 81.5345 188.609C61.5105 174.46 44.3642 156.595 30.9349 135.971C23.5009 124.46 17.2659 112.11 12.4697 99.0407C9.592 91.3668 7.19392 83.3332 5.27545 75.2996C2.03803 61.3907 0.359375 47.0022 0.359375 32.1341C0.359375 30.6953 0.359375 29.1365 0.359375 27.6977C6.35459 23.9806 12.7095 20.7432 19.0644 17.7456C20.7431 32.1341 24.1004 46.043 28.8966 59.3524C31.6544 66.9063 34.7719 74.3404 38.4889 81.4147C44.604 93.5251 52.0381 104.796 60.4314 115.228C75.1796 133.093 92.9254 148.321 113.069 160.072Z" fill="url(#paint0_linear_446_540)"/>
+<path d="M196.643 67.6256C195.084 76.3786 192.926 84.8918 190.168 93.1652C178.897 91.1269 167.266 90.0477 155.276 90.0477C154.197 90.0477 153.118 90.0477 152.039 90.0477C126.859 90.4074 102.878 95.6832 80.9352 105.036C72.302 94.8439 64.868 83.453 58.9927 71.3427C81.6546 61.8702 106.475 56.7144 132.614 56.7144C141.487 56.7144 150.24 57.3139 158.753 58.5129C171.823 60.1916 184.533 63.3091 196.643 67.6256Z" fill="url(#paint1_linear_446_540)"/>
+<path d="M199.64 34.0528C199.64 39.2087 199.401 44.3646 199.041 49.4005C186.691 44.1247 173.621 40.048 160.072 37.53C148.321 35.2518 136.211 34.0528 123.981 34.0528C97.7218 34.0528 72.6619 39.3286 49.88 48.9209C42.6858 51.9185 35.7313 55.3958 29.0167 59.2327C24.2205 46.0432 20.8632 32.0144 19.1846 17.6259C26.6186 14.1487 34.2925 11.271 42.2062 8.75301C60.3117 3.11751 79.4964 0 99.4005 0C119.904 0 139.568 3.23741 158.153 9.11272C172.782 13.789 186.691 20.2638 199.52 28.1775C199.64 30.2159 199.64 32.1343 199.64 34.0528Z" fill="url(#paint2_linear_446_540)"/>
+<path d="M190.168 93.2855C182.494 116.547 170.384 137.65 154.796 155.875C149.76 161.751 144.364 167.386 138.609 172.542C126.858 183.214 113.789 192.446 99.7601 200C93.4052 196.523 87.41 192.686 81.5347 188.609C93.0455 180.336 103.717 170.744 113.069 160.072C117.866 154.676 122.302 148.921 126.499 143.046C137.65 127.098 146.403 109.233 152.158 90.1679C153.237 90.1679 154.316 90.1679 155.396 90.1679C167.146 90.048 178.777 91.1272 190.168 93.2855Z" fill="url(#paint3_linear_446_540)"/>
+</g>
+<defs>
+<linearGradient id="paint0_linear_446_540" x1="15.8569" y1="27.5782" x2="86.4712" y2="182.06" gradientUnits="userSpaceOnUse">
+<stop stop-color="#57A4F7"/>
+<stop offset="1" stop-color="#2158F9"/>
+</linearGradient>
+<linearGradient id="paint1_linear_446_540" x1="58.9501" y1="80.8427" x2="196.648" y2="80.8427" gradientUnits="userSpaceOnUse">
+<stop stop-color="#2158F9"/>
+<stop offset="1" stop-color="#33E1E5"/>
+</linearGradient>
+<linearGradient id="paint2_linear_446_540" x1="19.1564" y1="29.6353" x2="199.647" y2="29.6353" gradientUnits="userSpaceOnUse">
+<stop stop-color="#255DF9"/>
+<stop offset="1" stop-color="#7C35BA"/>
+</linearGradient>
+<linearGradient id="paint3_linear_446_540" x1="95.3808" y1="192.567" x2="174.674" y2="97.4815" gradientUnits="userSpaceOnUse">
+<stop stop-color="#54A0F7"/>
+<stop offset="1" stop-color="#2158F9"/>
+</linearGradient>
+<clipPath id="clip0_446_540">
+<rect width="200" height="200" fill="white"/>
+</clipPath>
+</defs>
+</svg>
diff --git a/src/assets/icons/svg/waiting.svg b/src/assets/icons/svg/waiting.svg
new file mode 100644
index 0000000..2c2042d
--- /dev/null
+++ b/src/assets/icons/svg/waiting.svg
@@ -0,0 +1 @@
+<svg t="1716005941920" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6808" width="200" height="200"><path d="M739.555556 512a256 256 0 1 1 0 512 256 256 0 0 1 0-512z m18.887111-512a180.167111 180.167111 0 0 1 179.882666 169.870222l0.284445 10.24v311.068445a28.444444 28.444444 0 0 1-56.433778 5.12l-0.455111-5.12V180.110222a123.278222 123.278222 0 0 0-114.460445-122.936889l-8.817777-0.341333H209.237333a123.278222 123.278222 0 0 0-122.993777 114.460444l-0.284445 8.817778v662.641778c0 65.080889 50.460444 118.385778 114.460445 122.88l8.817777 0.341333h283.875556a28.444444 28.444444 0 0 1 5.12 56.433778l-5.12 0.455111h-283.875556a180.167111 180.167111 0 0 1-179.882666-169.927111l-0.284445-10.24V180.167111A180.167111 180.167111 0 0 1 198.997333 0.227556L209.237333 0h549.205334zM739.555556 568.888889a199.111111 199.111111 0 1 0 0 398.222222 199.111111 199.111111 0 0 0 0-398.222222z m115.712 314.026667a14.222222 14.222222 0 0 1 0 28.444444h-222.890667a14.222222 14.222222 0 0 1 0-28.444444h222.890667z m-45.738667-227.555556a74.126222 74.126222 0 0 1-37.660445 95.459556v24.234666c0 6.257778 5.12 11.377778 11.377778 11.377778h51.313778c19.057778-0.170667 34.645333 16.042667 34.929778 36.295111v25.486222a11.377778 11.377778 0 0 1-11.377778 11.377778h-228.579556a11.377778 11.377778 0 0 1-11.377777-11.377778v-25.486222c0.512-20.48 16.213333-36.693333 35.271111-36.295111h51.143111a11.377778 11.377778 0 0 0 11.377778-11.377778v-24.291555c-16.440889-7.793778-32.426667-21.674667-39.253334-38.570667a73.500444 73.500444 0 0 1 36.295111-95.459556c35.328-16.497778 81.123556 0.967111 96.540445 38.684445zM360.789333 682.666667a28.444444 28.444444 0 0 1 5.12 56.433777l-5.12 0.455112H199.111111a28.444444 28.444444 0 0 1-5.12-56.433778L199.111111 682.666667h161.678222z m113.777778-227.555556a28.444444 28.444444 0 0 1 5.12 56.433778L474.510222 512H199.111111a28.444444 28.444444 0 0 1-5.12-56.433778L199.111111 455.111111h275.456zM768 227.555556a28.444444 28.444444 0 0 1 5.12 56.433777L768 284.444444H199.111111a28.444444 28.444444 0 0 1-5.12-56.433777L199.111111 227.555556h568.888889z" fill="currentColor" p-id="6809"></path></svg>
\ No newline at end of file
diff --git a/src/assets/icons/svg/workflow.svg b/src/assets/icons/svg/workflow.svg
new file mode 100644
index 0000000..2f7423a
--- /dev/null
+++ b/src/assets/icons/svg/workflow.svg
@@ -0,0 +1 @@
+<svg t="1716004936483" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2712" width="200" height="200"><path d="M1024.99477 113.778v227.555a57.458 57.458 0 0 1-58.027 56.89H734.86277a57.458 57.458 0 0 1-58.027-56.89v-56.889H560.83877v455.112h115.996v-56.89a57.458 57.458 0 0 1 58.027-56.888h231.936a57.458 57.458 0 0 1 58.197 56.889v227.555a57.458 57.458 0 0 1-58.027 56.89H734.86277a57.458 57.458 0 0 1-58.027-56.89v-56.889H502.86877a57.458 57.458 0 0 1-58.027-56.889V568.89L274.51677 735.972a46.763 46.763 0 0 1-65.252 0l-195.754-192a44.658 44.658 0 0 1 0-64l195.754-192.057a46.763 46.763 0 0 1 65.252 0L445.01277 455.11V227.556a57.458 57.458 0 0 1 58.027-56.89h173.966v-56.888a57.458 57.458 0 0 1 58.026-56.89h231.936a57.458 57.458 0 0 1 58.027 56.89z" fill="currentColor" p-id="2713"></path></svg>
\ No newline at end of file
diff --git a/src/assets/styles/element-ui.scss b/src/assets/styles/element-ui.scss
index 43c093c..220e4ac 100644
--- a/src/assets/styles/element-ui.scss
+++ b/src/assets/styles/element-ui.scss
@@ -1,4 +1,15 @@
-// cover some element-ui styles
+
+.el-collapse {
+  .collapse__title {
+    font-weight: 600;
+    padding: 0 8px;
+    font-size: 1.2em;
+    line-height: 1.1em;
+  }
+  .el-collapse-item__content {
+    padding: 0 8px;
+  }
+}
 
 .el-divider--horizontal {
   margin-bottom: 10px;
@@ -68,6 +79,12 @@
       .el-dialog__body {
         padding: 15px !important;
       }
+      .el-dialog__header {
+        padding: 16px 16px 8px 16px;
+        box-sizing: border-box;
+        border-bottom: 1px solid var(--brder-color);
+        margin-right: 0;
+      }
     }
   }
 }
@@ -114,3 +131,19 @@
 .el-dropdown .el-dropdown-link {
   color: var(--el-color-primary) !important;
 }
+
+/* 褰� el-form 鐨� inline 灞炴�т负 true 鏃� */
+/* 璁剧疆 label 鐨勫搴﹂粯璁や负 68px */
+.el-form--inline .el-form-item__label {
+  width: 68px;
+}
+
+/* 璁剧疆 el-select 鐨勫搴﹂粯璁や负 240px */
+.el-form--inline .el-select {
+  width: 240px;
+}
+
+/* 璁剧疆 el-input 鐨勫搴﹂粯璁や负 240px */
+.el-form--inline .el-input {
+  width: 240px;
+}
diff --git a/src/assets/styles/index.scss b/src/assets/styles/index.scss
index 48865fb..bdf9b03 100644
--- a/src/assets/styles/index.scss
+++ b/src/assets/styles/index.scss
@@ -14,7 +14,14 @@
   -moz-osx-font-smoothing: grayscale;
   -webkit-font-smoothing: antialiased;
   text-rendering: optimizeLegibility;
-  font-family: Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
+  font-family:
+    Helvetica Neue,
+    Helvetica,
+    PingFang SC,
+    Hiragino Sans GB,
+    Microsoft YaHei,
+    Arial,
+    sans-serif;
 }
 
 label {
diff --git a/src/assets/styles/sidebar.scss b/src/assets/styles/sidebar.scss
index d47a2bd..d85da55 100644
--- a/src/assets/styles/sidebar.scss
+++ b/src/assets/styles/sidebar.scss
@@ -28,7 +28,10 @@
 
     // reset element-ui css
     .horizontal-collapse-transition {
-      transition: 0s width ease-in-out, 0s padding-left ease-in-out, 0s padding-right ease-in-out;
+      transition:
+        0s width ease-in-out,
+        0s padding-left ease-in-out,
+        0s padding-right ease-in-out;
     }
 
     .scrollbar-wrapper {
@@ -105,7 +108,6 @@
         background-color: rgba(0, 0, 0, 0.1) !important;
       }
     }
-
 
     & .theme-dark .nest-menu .el-sub-menu > .el-sub-menu__title,
     & .theme-dark .el-sub-menu .el-menu-item {
diff --git a/src/assets/styles/variables.module.scss b/src/assets/styles/variables.module.scss
index d07d3d4..92fc34d 100644
--- a/src/assets/styles/variables.module.scss
+++ b/src/assets/styles/variables.module.scss
@@ -13,6 +13,14 @@
   --fixedHeaderBg: #ffffff;
   --tableHeaderBg: #f8f8f9;
   --tableHeaderTextColor: #515a6e;
+
+  // 宸ヤ綔娴�
+  --bpmn-panel-border: #eeeeee;
+  --bpmn-panel-box-shadow: #cccccc;
+  --bpmn-panel-bar-background-color: #f5f7fa;
+
+  // ele
+  --brder-color: #e8e8e8
 }
 html.dark {
   --menuBg: #1d1e1f;
@@ -33,6 +41,26 @@
   .el-tree-node__content {
     --el-color-primary-light-9: #262727;
   }
+  // vxe-table 涓婚
+  --vxe-font-color: #98989E;
+  --vxe-primary-color: #2C7ECF;
+  --vxe-icon-background-color: #98989E;
+  --vxe-table-font-color: #98989E;
+  --vxe-table-resizable-color: #95969a;
+  --vxe-table-header-background-color: #28282A;
+  --vxe-table-body-background-color: #151518;
+  --vxe-table-background-color: #4a5663;
+  --vxe-table-border-width: 1px;
+  --vxe-table-border-color: #37373A;
+  --vxe-toolbar-background-color: #37373A;
+
+  // 宸ヤ綔娴�
+  --bpmn-panel-border: #37373A;
+  --bpmn-panel-box-shadow: #37373A;
+  --bpmn-panel-bar-background-color: #37373A;
+
+  // ele
+  --brder-color: #37373A
 }
 
 // base color
diff --git a/src/bpmn/assets/defaultXML.ts b/src/bpmn/assets/defaultXML.ts
new file mode 100644
index 0000000..dff0349
--- /dev/null
+++ b/src/bpmn/assets/defaultXML.ts
@@ -0,0 +1,23 @@
+function generateRandomValue() {
+  // 鐢熸垚涓�涓殢鏈烘暟
+  const randomValue = Math.random().toString(36).slice(2, 12);
+  return `Process_${randomValue}`;
+}
+
+const cartage: string = 'default';
+export default `<?xml version="1.0" encoding="UTF-8"?>
+<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:bioc="http://bpmn.io/schema/bpmn/biocolor/1.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:flowable="http://flowable.org/bpmn" targetNamespace="http://www.flowable.org/processdef">
+  <process id="process_${generateRandomValue()}" name="name_${generateRandomValue()}">
+    <startEvent id="startNode1" name="寮�濮�" />
+  </process>
+  <bpmndi:BPMNDiagram id="BPMNDiagram_flow">
+    <bpmndi:BPMNPlane id="BPMNPlane_flow" bpmnElement="T-2d89e7a3-ba79-4abd-9f64-ea59621c258c">
+      <bpmndi:BPMNShape id="BPMNShape_startNode1" bpmnElement="startNode1" bioc:stroke="">
+        <omgdc:Bounds x="240" y="200" width="30" height="30" />
+        <bpmndi:BPMNLabel>
+          <omgdc:Bounds x="242" y="237" width="23" height="14" />
+        </bpmndi:BPMNLabel>
+      </bpmndi:BPMNShape>
+    </bpmndi:BPMNPlane>
+  </bpmndi:BPMNDiagram>
+</definitions>`;
diff --git a/src/bpmn/assets/lang/zh.ts b/src/bpmn/assets/lang/zh.ts
new file mode 100644
index 0000000..ee0c5de
--- /dev/null
+++ b/src/bpmn/assets/lang/zh.ts
@@ -0,0 +1,126 @@
+export const NodeName = {
+  'bpmn:Process': '娴佺▼',
+  'bpmn:StartEvent': '寮�濮嬩簨浠�',
+  'bpmn:IntermediateThrowEvent': '涓棿浜嬩欢',
+  'bpmn:Task': '浠诲姟',
+  'bpmn:SendTask': '鍙戦�佷换鍔�',
+  'bpmn:ReceiveTask': '鎺ユ敹浠诲姟',
+  'bpmn:UserTask': '鐢ㄦ埛浠诲姟',
+  'bpmn:ManualTask': '鎵嬪伐浠诲姟',
+  'bpmn:BusinessRuleTask': '涓氬姟瑙勫垯浠诲姟',
+  'bpmn:ServiceTask': '鏈嶅姟浠诲姟',
+  'bpmn:ScriptTask': '鑴氭湰浠诲姟',
+  'bpmn:EndEvent': '缁撴潫浜嬩欢',
+  'bpmn:SequenceFlow': '娴佺▼绾�',
+  'bpmn:ExclusiveGateway': '浜掓枼缃戝叧',
+  'bpmn:ParallelGateway': '骞惰缃戝叧',
+  'bpmn:InclusiveGateway': '鐩稿缃戝叧',
+  'bpmn:ComplexGateway': '澶嶆潅缃戝叧',
+  'bpmn:EventBasedGateway': '浜嬩欢缃戝叧',
+  'bpmn:Participant': '姹�/鍙備笌鑰�',
+  'bpmn:SubProcess': '瀛愭祦绋�',
+  'bpmn:DataObjectReference': '鏁版嵁瀵硅薄寮曠敤',
+  'bpmn:DataStoreReference': '鏁版嵁瀛樺偍寮曠敤',
+  'bpmn:Group': '缁�'
+};
+
+export default {
+  'Activate hand tool': '鍚姩鎵嬪姩宸ュ叿',
+  'Activate lasso tool': '鍚姩 Lasso 宸ュ叿',
+  'Activate create/remove space tool': '鍚姩鍒涘缓/鍒犻櫎绌洪棿宸ュ叿',
+  'Activate global connect tool': '鍚姩鍏ㄥ眬杩炴帴宸ュ叿',
+  'Ad-hoc': 'Ad-hoc',
+  'Add lane above': '鍦ㄤ笂鏂规坊鍔犳吵閬�',
+  'Add lane below': '鍦ㄤ笅鏂规坊鍔犳吵閬�',
+  'Business rule task': '瑙勫垯浠诲姟',
+  'Call activity': '寮曠敤娴佺▼',
+  'Compensation end event': '缁撴潫琛ュ伩浜嬩欢',
+  'Compensation intermediate throw event': '涓棿琛ュ伩鎶涘嚭浜嬩欢',
+  'Complex gateway': '澶嶆潅缃戝叧',
+  'Conditional intermediate catch event': '涓棿鏉′欢鎹曡幏浜嬩欢',
+  'Conditional start event (non-interrupting)': '鏉′欢鍚姩浜嬩欢 (闈炰腑鏂�)',
+  'Conditional start event': '鏉′欢鍚姩浜嬩欢',
+  'Connect using association': '鏂囨湰鍏宠仈',
+  'Connect using sequence/message flow or association': '娑堟伅鍏宠仈',
+  'Change element': '鏇存敼鍏冪礌',
+  'Change type': '鏇存敼绫诲瀷',
+  'Create data object reference': '鍒涘缓鏁版嵁瀵硅薄寮曠敤',
+  'Create data store reference': '鍒涘缓鏁版嵁瀛樺偍寮曠敤',
+  'Create expanded sub-process': '鍒涘缓鍙姌鍙犲瓙娴佺▼',
+  'Create pool/participant': '鍒涘缓姹�/鍙備笌鑰�',
+  'Collection': '闆嗗悎',
+  'Connect using data input association': '鏁版嵁杈撳叆鍏宠仈',
+  'Data store reference': '鏁版嵁瀛樺偍寮曠敤',
+  'Data object reference': '鏁版嵁瀵硅薄寮曠敤',
+  'Divide into two lanes': '鍒嗘垚涓や釜娉抽亾',
+  'Divide into three lanes': '鍒嗘垚涓変釜娉抽亾',
+  'End event': '缁撴潫浜嬩欢',
+  'Error end event': '缁撴潫閿欒浜嬩欢',
+  'Escalation end event': '缁撴潫鍗囩骇浜嬩欢',
+  'Escalation intermediate throw event': '涓棿鍗囩骇鎶涘嚭浜嬩欢',
+  'Event sub-process': '浜嬩欢瀛愭祦绋�',
+  'Event-based gateway': '浜嬩欢缃戝叧',
+  'Exclusive gateway': '浜掓枼缃戝叧',
+  'Empty pool/participant (removes content)': '娓呯┖姹�/鍙備笌鑰� (鍒犻櫎鍐呭)',
+  'Empty pool/participant': '娓呯┖姹�/鍙備笌鑰�',
+  'Expanded pool/participant': '灞曞紑姹�/鍙備笌鑰�',
+  'Inclusive gateway': '鐩稿缃戝叧',
+  'Intermediate throw event': '涓棿鎶涘嚭浜嬩欢',
+  'Loop': '寰幆',
+  'Link intermediate catch event': '涓棿閾炬帴鎹曡幏浜嬩欢',
+  'Link intermediate throw event': '涓棿閾炬帴鎶涘嚭浜嬩欢',
+  'Manual task': '鎵嬪姩浠诲姟',
+  'Message end event': '缁撴潫娑堟伅浜嬩欢',
+  'Message intermediate catch event': '涓棿娑堟伅鎹曡幏浜嬩欢',
+  'Message intermediate throw event': '涓棿娑堟伅鎶涘嚭浜嬩欢',
+  'Message start event': '娑堟伅鍚姩浜嬩欢',
+  'Parallel gateway': '骞惰缃戝叧',
+  'Parallel multi-instance': '骞惰澶氬疄渚�',
+  'Participant multiplicity': '鍙備笌鑰呭閲嶆��',
+  'Receive task': '鎺ュ彈浠诲姟',
+  'Remove': '绉婚櫎',
+  'Script task': '鑴氭湰浠诲姟',
+  'Send task': '鍙戦�佷换鍔�',
+  'Sequential multi-instance': '涓茶澶氬疄渚�',
+  'Service task': '鏈嶅姟浠诲姟',
+  'Signal end event': '缁撴潫淇″彿浜嬩欢',
+  'Signal intermediate catch event': '涓棿淇″彿鎹曡幏浜嬩欢',
+  'Signal intermediate throw event': '涓棿淇″彿鎶涘嚭浜嬩欢',
+  'Signal start event (non-interrupting)': '淇″彿鍚姩浜嬩欢 (闈炰腑鏂�)',
+  'Signal start event': '淇″彿鍚姩浜嬩欢',
+  'Start event': '寮�濮嬩簨浠�',
+  'Sub-process (collapsed)': '鍙姌鍙犲瓙娴佺▼',
+  'Sub-process (expanded)': '鍙睍寮�瀛愭祦绋�',
+  'Sub rocess': '瀛愭祦绋�',
+  'Task': '浠诲姟',
+  'Transaction': '浜嬪姟',
+  'Terminate end event': '缁堟杈圭晫浜嬩欢',
+  'Timer intermediate catch event': '涓棿瀹氭椂鎹曡幏浜嬩欢',
+  'Timer start event (non-interrupting)': '瀹氭椂鍚姩浜嬩欢 (闈炰腑鏂�)',
+  'Timer start event': '瀹氭椂鍚姩浜嬩欢',
+  'User task': '鐢ㄦ埛浠诲姟',
+  'Create start event': '鍒涘缓寮�濮嬩簨浠�',
+  'Create gateway': '鍒涘缓缃戝叧',
+  'Create intermediate/boundary event': '鍒涘缓涓棿/杈圭晫浜嬩欢',
+  'Create end event': '鍒涘缓缁撴潫浜嬩欢',
+  'Create group': '鍒涘缓缁�',
+  'Create startEvent': '寮�濮嬭妭鐐�',
+  'Create endEvent': '缁撴潫鑺傜偣',
+  'Create exclusiveGateway': '浜掓枼缃戝叧',
+  'Create parallelGateway': '骞惰缃戝叧',
+  'Create task': '浠诲姟鑺傜偣',
+  'Create userTask': '鐢ㄦ埛浠诲姟鑺傜偣',
+  'Condition type': '鏉′欢绫诲瀷',
+  'Append end event': '杩藉姞缁撴潫浜嬩欢鑺傜偣',
+  'Append gateway': '杩藉姞缃戝叧鑺傜偣',
+  'Append task': '杩藉姞浠诲姟',
+  'Append user task': '杩藉姞鐢ㄦ埛浠诲姟鑺傜偣',
+  'Append text annotation': '杩藉姞鏂囨湰娉ㄩ噴',
+  'Append intermediate/boundary event': '杩藉姞涓棿鎴栬竟鐣屼簨浠�',
+  'Append receive task': '杩藉姞鎺ユ敹浠诲姟鑺傜偣',
+  'Append message intermediate catch event': '杩藉姞涓棿娑堟伅鎹曡幏浜嬩欢',
+  'Append timer intermediate catch event': '杩藉姞涓棿瀹氭椂鎹曡幏浜嬩欢',
+  'Append conditional intermediate catch event': '杩藉姞涓棿鏉′欢鎹曡幏浜嬩欢',
+  'Append signal intermediate catch event': '杩藉姞涓棿淇″彿鎹曡幏浜嬩欢',
+  'flow elements must be children of pools/participants': '娴佺▼鍏冪礌蹇呴』鏄睜/鍙備笌鑰呯殑瀛愬厓绱�'
+};
diff --git a/src/bpmn/assets/moddle/flowable.ts b/src/bpmn/assets/moddle/flowable.ts
new file mode 100644
index 0000000..de959a6
--- /dev/null
+++ b/src/bpmn/assets/moddle/flowable.ts
@@ -0,0 +1,1250 @@
+export default {
+  'name': 'Flowable',
+  'uri': 'http://flowable.org/bpmn',
+  'prefix': 'flowable',
+  'xml': {
+    'tagAlias': 'lowerCase'
+  },
+  'associations': [],
+  'types': [
+    {
+      'name': 'flowable:extCandidateUsers',
+      'isAbstract': true,
+      'extends': [],
+      'superClass': ['Element'],
+      'meta': {
+        'allowedIn': ['*']
+      },
+      'properties': [
+        {
+          'name': 'body',
+          'type': 'String',
+          'isBody': true
+        }
+      ]
+    },
+    {
+      'name': 'flowable:extAssignee',
+      'isAbstract': true,
+      'extends': [],
+      'superClass': ['Element'],
+      'meta': {
+        'allowedIn': ['*']
+      },
+      'properties': [
+        {
+          'name': 'body',
+          'type': 'String',
+          'isBody': true
+        }
+      ]
+    },
+    {
+      'name': 'flowable:property',
+      'superClass': ['Element'],
+      'properties': [
+        {
+          'name': 'id',
+          'isAttr': true,
+          'type': 'String'
+        },
+        {
+          'name': 'name',
+          'isAttr': true,
+          'type': 'String'
+        },
+        {
+          'name': 'value',
+          'isAttr': true,
+          'type': 'String'
+        }
+      ]
+    },
+    {
+      'name': 'flowable:properties',
+      'isAbstract': true,
+      'extends': [],
+      'superClass': ['Element'],
+      'meta': {
+        'allowedIn': ['*']
+      },
+      'properties': [
+        {
+          'name': 'values',
+          'type': 'flowable:property',
+          'isMany': true
+        }
+      ]
+    },
+    {
+      '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': 'flowable:in',
+      'superClass': ['Element'],
+      'properties': [
+        {
+          'name': 'source',
+          'type': 'string',
+          'isAttr': true
+        },
+        {
+          'name': 'target',
+          'type': 'string',
+          'isAttr': true
+        }
+      ]
+    },
+    {
+      'name': 'flowable:out',
+      'superClass': ['Element'],
+      'properties': [
+        {
+          'name': 'source',
+          'type': 'string',
+          'isAttr': true
+        },
+        {
+          'name': 'target',
+          'type': 'string',
+          'isAttr': true
+        }
+      ]
+    },
+    {
+      'name': 'BoundaryEvent',
+      'superClass': ['CatchEvent'],
+      'properties': [
+        {
+          'name': 'cancelActivity',
+          'default': true,
+          'isAttr': true,
+          'type': 'Boolean'
+        },
+        {
+          'name': 'attachedToRef',
+          'type': 'Activity',
+          'isAttr': true,
+          'isReference': 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': 'UserTask',
+      'isAbstract': true,
+      'extends': ['bpmn:UserTask'],
+      'properties': [
+        {
+          'name': 'timerEventDefinition',
+          'type': 'Expression'
+        },
+        {
+          'name': 'multiInstanceLoopCharacteristics',
+          'type': 'MultiInstanceLoopCharacteristics'
+        }
+      ]
+    },
+    {
+      'name': 'StartEvent',
+      'isAbstract': true,
+      'extends': ['bpmn:StartEvent'],
+      'properties': [
+        {
+          'name': 'timerEventDefinition',
+          'type': 'Expression'
+        }
+      ]
+    },
+    {
+      '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': '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': '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': 'ExclusiveGateway',
+      'isAbstract': true,
+      'extends': ['bpmn:ExclusiveGateway'],
+      'properties': [
+        {
+          'name': 'serviceClass',
+          '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': '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']
+      },
+      'properties': [
+        {
+          'name': 'name',
+          'isAttr': true,
+          'type': 'String'
+        },
+        {
+          'name': 'expression',
+          'isAttr': true,
+          'type': 'expression'
+        },
+        {
+          'name': 'string',
+          'type': 'string'
+        },
+        {
+          'name': 'stringValue',
+          'isAttr': true,
+          'type': 'String'
+        }
+      ]
+    },
+    {
+      'name': 'string',
+      'superClass': ['Element'],
+      'meta': {
+        'allowedIn': ['flowable:Field']
+      },
+      'properties': [
+        {
+          'name': 'body',
+          'isBody': true,
+          'type': 'String'
+        }
+      ]
+    },
+    {
+      'name': 'expression',
+      'superClass': ['Element'],
+      'meta': {
+        'allowedIn': ['flowable:Field']
+      },
+      'properties': [
+        {
+          'name': 'body',
+          'isBody': true,
+          'type': 'String'
+        }
+      ]
+    },
+    {
+      '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': 'SequenceFlow',
+      'superClass': ['FlowElement'],
+      'properties': [
+        {
+          'name': 'isImmediate',
+          'isAttr': true,
+          'type': 'Boolean'
+        },
+        {
+          'name': 'conditionExpression',
+          'type': 'Expression'
+        },
+        {
+          'name': 'sourceRef',
+          'type': 'FlowNode',
+          'isAttr': true,
+          'isReference': true
+        },
+        {
+          'name': 'targetRef',
+          'type': 'FlowNode',
+          'isAttr': true,
+          'isReference': true
+        }
+      ]
+    },
+    {
+      'name': 'MultiInstanceLoopCharacteristics',
+      'superClass': ['LoopCharacteristics'],
+      'properties': [
+        {
+          'name': 'isSequential',
+          'default': false,
+          'isAttr': true,
+          'type': 'Boolean'
+        },
+        {
+          'name': 'behavior',
+          'type': 'MultiInstanceBehavior',
+          'default': 'All',
+          'isAttr': true
+        },
+        {
+          'name': 'loopCardinality',
+          'type': 'Expression',
+          'xml': {
+            'serialize': 'xsi:type'
+          }
+        },
+        {
+          'name': 'loopDataInputRef',
+          'type': 'ItemAwareElement',
+          'isReference': true
+        },
+        {
+          'name': 'loopDataOutputRef',
+          'type': 'ItemAwareElement',
+          'isReference': true
+        },
+        {
+          'name': 'inputDataItem',
+          'type': 'DataInput',
+          'xml': {
+            'serialize': 'property'
+          }
+        },
+        {
+          'name': 'outputDataItem',
+          'type': 'DataOutput',
+          'xml': {
+            'serialize': 'property'
+          }
+        },
+        {
+          'name': 'complexBehaviorDefinition',
+          'type': 'ComplexBehaviorDefinition',
+          'isMany': true
+        },
+        {
+          'name': 'completionCondition',
+          'type': 'Expression',
+          'xml': {
+            'serialize': 'xsi:type'
+          }
+        },
+        {
+          'name': 'oneBehaviorEventRef',
+          'type': 'EventDefinition',
+          'isAttr': true,
+          'isReference': true
+        },
+        {
+          'name': 'noneBehaviorEventRef',
+          'type': 'EventDefinition',
+          'isAttr': true,
+          'isReference': true
+        }
+      ]
+    },
+    {
+      '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': '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'
+        }
+      ]
+    }
+  ],
+  'emumerations': []
+};
diff --git a/src/bpmn/assets/module/ContextPad/CustomContextPadProvider.ts b/src/bpmn/assets/module/ContextPad/CustomContextPadProvider.ts
new file mode 100644
index 0000000..9f12ff2
--- /dev/null
+++ b/src/bpmn/assets/module/ContextPad/CustomContextPadProvider.ts
@@ -0,0 +1,138 @@
+import ContextPadProvider from 'bpmn-js/lib/features/context-pad/ContextPadProvider';
+import { Injector } from 'didi';
+import EventBus from 'diagram-js/lib/core/EventBus';
+import ContextPad from 'diagram-js/lib/features/context-pad/ContextPad';
+import Modeling from 'bpmn-js/lib/features/modeling/Modeling.js';
+import ElementFactory from 'bpmn-js/lib/features/modeling/ElementFactory';
+import Connect from 'diagram-js/lib/features/connect/Connect';
+import Create from 'diagram-js/lib/features/create/Create';
+import PopupMenu from 'diagram-js/lib/features/popup-menu/PopupMenu';
+import Canvas from 'diagram-js/lib/core/Canvas';
+import Rules from 'diagram-js/lib/features/rules/Rules';
+import { Element, Shape } from 'diagram-js/lib/model/Types';
+import BpmnFactory from 'bpmn-js/lib/features/modeling/BpmnFactory';
+import modeler from '@/store/modules/modeler';
+
+// @Description: 澧炲己鍏冪礌杩炵嚎浜嬩欢
+
+class CustomContextPadProvider extends ContextPadProvider {
+  private _contextPad: ContextPad;
+  private _modeling: Modeling;
+  private _elementFactory: ElementFactory;
+  private _autoPlace: any;
+  private _connect: Connect;
+  private _create: Create;
+  private _popupMenu: PopupMenu;
+  private _canvas: Canvas;
+  private _rules: Rules;
+
+  constructor(
+    config: any,
+    injector: Injector,
+    eventBus: EventBus,
+    contextPad: ContextPad,
+    modeling: Modeling,
+    elementFactory: ElementFactory,
+    connect: Connect,
+    create: Create,
+    popupMenu: PopupMenu,
+    canvas: Canvas,
+    rules: Rules,
+    translate
+  ) {
+    // @ts-ignore
+    super(config, injector, eventBus, contextPad, modeling, elementFactory, connect, create, popupMenu, canvas, rules, translate);
+
+    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._autoPlace = injector.get('autoPlace', false);
+  }
+
+  getContextPadEntries(element: Element) {
+    const actions: Record<string, any> = {};
+
+    const appendUserTask = (event: Event, element: Shape) => {
+      const shape = this._elementFactory.createShape({ type: 'bpmn:UserTask' });
+      this._create.start(event, shape, {
+        source: element
+      });
+    };
+
+    const appendMultiInstanceUserTask = (event: Event, element: Shape) => {
+      const store = modeler();
+      const bpmnFactory = store.getModeler().get('bpmnFactory') as BpmnFactory;
+      const businessObject = bpmnFactory.create('bpmn:UserTask', {
+        // name: '澶氬疄渚嬬敤鎴蜂换鍔�',
+        isForCompensation: false
+      });
+      businessObject.loopCharacteristics = bpmnFactory.create('bpmn:MultiInstanceLoopCharacteristics');
+      // 鍒涘缓 Shape
+      const shape = this._elementFactory.createShape({
+        type: 'bpmn:UserTask',
+        businessObject: businessObject
+      });
+      this._create.start(event, shape, { source: element });
+    };
+
+    const appendTask = this._autoPlace
+      ? (event, element) => {
+          const bpmnFactory: BpmnFactory | undefined = modeler().getModeler().get('bpmnFactory');
+          const businessObject = bpmnFactory.create('bpmn:UserTask', {
+            // name: '澶氬疄渚嬬敤鎴蜂换鍔�',// 鍙抽敭鍒涘缓鏄剧ず
+            isForCompensation: false
+          });
+
+          // 鍒涘缓澶氬疄渚嬪睘鎬у苟鍒嗛厤缁欑敤鎴蜂换鍔$殑 loopCharacteristics
+          businessObject.loopCharacteristics = bpmnFactory.create('bpmn:MultiInstanceLoopCharacteristics');
+
+          // 鍒涘缓 Shape
+          const shape = this._elementFactory.createShape({
+            type: 'bpmn:UserTask',
+            businessObject: businessObject
+          });
+
+          this._autoPlace.append(element, shape);
+        }
+      : appendMultiInstanceUserTask;
+
+    const append = this._autoPlace
+      ? (event: Event, element: Shape) => {
+          const shape = this._elementFactory.createShape({ type: 'bpmn:UserTask' });
+          this._autoPlace.append(element, shape);
+        }
+      : appendUserTask;
+
+    // // 娣诲姞鍒涘缓鐢ㄦ埛浠诲姟鎸夐挳
+    actions['append.append-user-task'] = {
+      group: 'model',
+      className: 'bpmn-icon-user-task',
+      title: '鐢ㄦ埛浠诲姟',
+      action: {
+        dragstart: appendUserTask,
+        click: append
+      }
+    };
+
+    // 娣诲姞鍒涘缓澶氬疄渚嬬敤鎴蜂换鍔℃寜閽�
+    actions['append.append-multi-instance-user-task'] = {
+      group: 'model',
+      className: 'bpmn-icon-user', // 浣犲彲浠ヤ娇鐢ㄥ瀹炰緥鐢ㄦ埛浠诲姟鐨勫浘鏍�  bpmn-icon-user   bpmn-icon-user-task
+      title: '澶氬疄渚嬬敤鎴蜂换鍔�',
+      action: {
+        dragstart: appendMultiInstanceUserTask,
+        click: appendTask
+      }
+    };
+
+    return actions;
+  }
+}
+
+export default CustomContextPadProvider;
diff --git a/src/bpmn/assets/module/Palette/CustomPaletteProvider.ts b/src/bpmn/assets/module/Palette/CustomPaletteProvider.ts
new file mode 100644
index 0000000..8556d9b
--- /dev/null
+++ b/src/bpmn/assets/module/Palette/CustomPaletteProvider.ts
@@ -0,0 +1,109 @@
+import { assign } from 'min-dash';
+import PaletteProvider from 'bpmn-js/lib/features/palette/PaletteProvider';
+import ElementFactory from 'bpmn-js/lib/features/modeling/ElementFactory';
+import Create from 'diagram-js/lib/features/create/Create';
+import SpaceTool from 'diagram-js/lib/features/space-tool/SpaceTool';
+import LassoTool from 'diagram-js/lib/features/lasso-tool/LassoTool';
+import HandTool from 'diagram-js/lib/features/hand-tool/HandTool';
+import GlobalConnect from 'diagram-js/lib/features/global-connect/GlobalConnect';
+import Palette from 'diagram-js/lib/features/palette/Palette';
+import modeler from '@/store/modules/modeler';
+import BpmnFactory from 'bpmn-js/lib/features/modeling/BpmnFactory';
+
+// @Description: 澧炲己宸︿晶闈㈡澘
+class CustomPaletteProvider extends PaletteProvider {
+  private readonly _palette: Palette;
+  private readonly _create: Create;
+  private readonly _elementFactory: ElementFactory;
+  private readonly _spaceTool: SpaceTool;
+  private readonly _lassoTool: LassoTool;
+  private readonly _handTool: HandTool;
+  private readonly _globalConnect: GlobalConnect;
+  private readonly _translate: any;
+
+  constructor(palette, create, elementFactory, spaceTool, lassoTool, handTool, globalConnect, translate) {
+    super(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;
+  }
+
+  getPaletteEntries() {
+    const actions = {},
+      create = this._create,
+      elementFactory = this._elementFactory,
+      translate = this._translate;
+
+    function createAction(type: string, group: string, className: string, title: string, options?: object) {
+      function createListener(event) {
+        const shape = elementFactory.createShape(assign({ type: type }, options));
+        if (options) {
+          !shape.businessObject.di && (shape.businessObject.di = {});
+          shape.businessObject.di.isExpanded = (options as { [key: string]: any }).isExpanded;
+        }
+        create.start(event, shape, null);
+      }
+      const shortType = type.replace(/^bpmn:/, '');
+      return {
+        group: group,
+        className: className,
+        title: title || translate('Create {type}', { type: shortType }),
+        action: {
+          dragstart: createListener,
+          click: createListener
+        }
+      };
+    }
+
+    function createMultiInstanceUserTask(event) {
+      const bpmnFactory: BpmnFactory | undefined = modeler().getBpmnFactory();
+      // 鍒涘缓涓�涓� bpmn:UserTask
+      const userTask = bpmnFactory.create('bpmn:UserTask', {
+        // name: '澶氬疄渚嬬敤鎴蜂换鍔�', // 鍦ㄧ敾鏉夸腑鏄剧ず瀛楁
+        isForCompensation: false
+      });
+      // 灏嗗瀹炰緥灞炴�у垎閰嶇粰 bpmn:UserTask 鐨� loopCharacteristics
+      userTask.loopCharacteristics = bpmnFactory.create('bpmn:MultiInstanceLoopCharacteristics');
+      const customUserTask = elementFactory.createShape({
+        type: 'bpmn:UserTask',
+        businessObject: userTask // 鍒嗛厤鍒涘缓鐨� userTask 鍒� businessObject
+      });
+      create.start(event, customUserTask, {});
+    }
+
+    assign(actions, {
+      'create.parallel-gateway': createAction('bpmn:ParallelGateway', 'gateway', 'bpmn-icon-gateway-parallel', '骞惰缃戝叧'),
+      'create.event-base-gateway': createAction('bpmn:EventBasedGateway', 'gateway', 'bpmn-icon-gateway-eventbased', '浜嬩欢缃戝叧'),
+      // 鍒嗙粍绾�
+      'gateway-separator': {
+        group: 'gateway',
+        separator: true
+      },
+      'create.user-task': createAction('bpmn:UserTask', 'activity', 'bpmn-icon-user-task', '鍒涘缓鐢ㄦ埛浠诲姟'),
+      'create.multi-instance-user-task': {
+        group: 'activity',
+        type: 'bpmn:UserTask',
+        className: 'bpmn-icon-user task-multi-instance',
+        title: '鍒涘缓澶氬疄渚嬬敤鎴蜂换鍔�',
+        action: {
+          click: createMultiInstanceUserTask,
+          dragstart: createMultiInstanceUserTask
+        }
+      },
+      'task-separator': {
+        group: 'activity',
+        separator: true
+      }
+    });
+    return actions;
+  }
+}
+
+CustomPaletteProvider['$inject'] = ['palette', 'create', 'elementFactory', 'spaceTool', 'lassoTool', 'handTool', 'globalConnect', 'translate'];
+
+export default CustomPaletteProvider;
diff --git a/src/bpmn/assets/module/Renderer/CustomRenderer.ts b/src/bpmn/assets/module/Renderer/CustomRenderer.ts
new file mode 100644
index 0000000..6a4eb1a
--- /dev/null
+++ b/src/bpmn/assets/module/Renderer/CustomRenderer.ts
@@ -0,0 +1,56 @@
+import BaseRenderer from 'diagram-js/lib/draw/BaseRenderer';
+import {
+  append as svgAppend,
+  attr as svgAttr,
+  create as svgCreate,
+  select as svgSelect,
+  selectAll as svgSelectAll,
+  clone as svgClone,
+  clear as svgClear,
+  remove as svgRemove
+} from 'tiny-svg';
+
+const HIGH_PRIORITY = 1500;
+export default class CustomRenderer extends BaseRenderer {
+  bpmnRenderer: BaseRenderer;
+  modeling: any;
+  constructor(eventBus, bpmnRenderer, modeling) {
+    super(eventBus, HIGH_PRIORITY);
+    this.bpmnRenderer = bpmnRenderer;
+    this.modeling = modeling;
+  }
+  canRender(element) {
+    // ignore labels
+    return !element.labelTarget;
+  }
+
+  /**
+   * 鑷畾涔夎妭鐐瑰浘褰�
+   * @param {*} parentNode 褰撳墠鍏冪礌鐨剆vgNode
+   * @param {*} element
+   * @returns
+   */
+  drawShape(parentNode, element) {
+    const shape = this.bpmnRenderer.drawShape(parentNode, element);
+    const { type, width, height } = element;
+    // 寮�濮� 濉厖缁胯壊
+    if (type === 'bpmn:StartEvent') {
+      svgAttr(shape, { fill: '#77DF6D' });
+      return shape;
+    }
+    if (type === 'bpmn:EndEvent') {
+      svgAttr(shape, { fill: '#EE7B77' });
+      return shape;
+    }
+    if (type === 'bpmn:UserTask') {
+      svgAttr(shape, { fill: '#A9C4F8' });
+      return shape;
+    }
+    return shape;
+  }
+
+  getShapePath(shape) {
+    return this.bpmnRenderer.getShapePath(shape);
+  }
+}
+CustomRenderer['$inject'] = ['eventBus', 'bpmnRenderer'];
diff --git a/src/bpmn/assets/module/Translate/index.ts b/src/bpmn/assets/module/Translate/index.ts
new file mode 100644
index 0000000..6b52dae
--- /dev/null
+++ b/src/bpmn/assets/module/Translate/index.ts
@@ -0,0 +1,15 @@
+import zh from '../../lang/zh';
+
+const customTranslate = (template: any, replacements: any) => {
+  replacements = replacements || {};
+  template = zh[template] || template;
+  return template.replace(/{([^}]+)}/g, function (_: any, key: any) {
+    return replacements[key] || '{' + key + '}';
+  });
+};
+
+export const translateModule = {
+  translate: ['value', customTranslate]
+};
+
+export default translateModule;
diff --git a/src/bpmn/assets/module/index.ts b/src/bpmn/assets/module/index.ts
new file mode 100644
index 0000000..55f6b9f
--- /dev/null
+++ b/src/bpmn/assets/module/index.ts
@@ -0,0 +1,17 @@
+// 缈昏瘧妯″潡
+import TranslationModule from './Translate';
+import { ModuleDeclaration } from 'didi';
+import CustomPaletteProvider from './Palette/CustomPaletteProvider';
+import CustomRenderer from './Renderer/CustomRenderer';
+import CustomContextPadProvider from './ContextPad/CustomContextPadProvider';
+
+const Module: ModuleDeclaration[] = [
+  {
+    __init__: ['customPaletteProvider', 'customContextPadProvider', 'customRenderer'],
+    customPaletteProvider: ['type', CustomPaletteProvider],
+    customRenderer: ['type', CustomRenderer],
+    customContextPadProvider: ['type', CustomContextPadProvider]
+  },
+  TranslationModule
+];
+export default Module;
diff --git a/src/bpmn/assets/showConfig.ts b/src/bpmn/assets/showConfig.ts
new file mode 100644
index 0000000..853eb35
--- /dev/null
+++ b/src/bpmn/assets/showConfig.ts
@@ -0,0 +1,50 @@
+export default {
+  'bpmn:EndEvent': {},
+  'bpmn:StartEvent': {
+    initiator: true,
+    formKey: true
+  },
+  'bpmn:UserTask': {
+    allocationType: true,
+    specifyDesc: true,
+    multipleUserAuditType: true,
+    async: true,
+    priority: true,
+    skipExpression: true,
+    dueDate: true,
+    taskListener: true,
+    executionListener: true
+  },
+  'bpmn:ServiceTask': {
+    async: true,
+    skipExpression: true,
+    isForCompensation: true,
+    triggerable: true,
+    class: true
+  },
+  'bpmn:ScriptTask': {
+    async: true,
+    isForCompensation: true,
+    autoStoreVariables: true
+  },
+  'bpmn:ManualTask': {
+    async: true,
+    isForCompensation: true
+  },
+  'bpmn:ReceiveTask': {
+    async: true,
+    isForCompensation: true
+  },
+  'bpmn:SendTask': {
+    async: true,
+    isForCompensation: true
+  },
+  'bpmn:BusinessRuleTask': {
+    async: true,
+    isForCompensation: true,
+    ruleVariablesInput: true,
+    rules: true,
+    resultVariable: true,
+    exclude: true
+  }
+};
diff --git a/src/bpmn/assets/style/index.scss b/src/bpmn/assets/style/index.scss
new file mode 100644
index 0000000..3c92792
--- /dev/null
+++ b/src/bpmn/assets/style/index.scss
@@ -0,0 +1,284 @@
+.djs-palette {
+  width: 300px;
+
+  .bpmn-icon-hand-tool:hover {
+    &:after {
+      content: '鍚姩鎵嬪姩宸ュ叿';
+      position: absolute;
+      left: 45px;
+      width: 120px;
+      font-size: 15px;
+      font-weight: bold;
+      color: #3a84de;
+      border-radius: 2px;
+      border: 1px solid #cccccc;
+      background-color: #fafafa;
+      opacity: 0.8;
+    }
+  }
+  .bpmn-icon-lasso-tool:hover {
+    &:after {
+      content: '鍚姩濂楃储宸ュ叿';
+      position: absolute;
+      left: 100px;
+      width: 120px;
+      font-size: 15px;
+      font-weight: bold;
+      color: #3a84de;
+      border-radius: 2px;
+      border: 1px solid #cccccc;
+      background-color: #fafafa;
+      opacity: 0.8;
+    }
+  }
+  .bpmn-icon-space-tool:hover {
+    &:after {
+      content: '鍚姩鍒涘缓/鍒犻櫎绌洪棿宸ュ叿';
+      position: absolute;
+      left: 45px;
+      width: 170px;
+      font-size: 15px;
+      font-weight: bold;
+      color: #3a84de;
+      border-radius: 2px;
+      border: 1px solid #cccccc;
+      background-color: #fafafa;
+      opacity: 0.8;
+    }
+  }
+  .bpmn-icon-connection-multi:hover {
+    &:after {
+      content: '鍚姩鍏ㄥ眬杩炴帴宸ュ叿';
+      position: absolute;
+      left: 100px;
+      width: 140px;
+      font-size: 15px;
+      font-weight: bold;
+      color: #3a84de;
+      border-radius: 2px;
+      border: 1px solid #cccccc;
+      background-color: #fafafa;
+      opacity: 0.8;
+    }
+  }
+  .bpmn-icon-start-event-none:hover {
+    &:after {
+      content: '鍒涘缓寮�濮嬩簨浠�';
+      position: absolute;
+      left: 45px;
+      width: 120px;
+      font-size: 15px;
+      font-weight: bold;
+      color: #3a84de;
+      border-radius: 2px;
+      border: 1px solid #cccccc;
+      background-color: #fafafa;
+      opacity: 0.8;
+    }
+  }
+  .bpmn-icon-intermediate-event-none:hover {
+    &:after {
+      content: '鍒涘缓涓棿/杈圭晫浜嬩欢';
+      position: absolute;
+      left: 100px;
+      width: 140px;
+      font-size: 15px;
+      font-weight: bold;
+      color: #3a84de;
+      border-radius: 2px;
+      border: 1px solid #cccccc;
+      background-color: #fafafa;
+      opacity: 0.8;
+    }
+  }
+  .bpmn-icon-end-event-none:hover {
+    &:after {
+      content: '鍒涘缓缁撴潫浜嬩欢';
+      position: absolute;
+      left: 45px;
+      width: 120px;
+      font-size: 15px;
+      font-weight: bold;
+      color: #3a84de;
+      border-radius: 2px;
+      border: 1px solid #cccccc;
+      background-color: #fafafa;
+      opacity: 0.8;
+    }
+  }
+  .bpmn-icon-gateway-none:hover {
+    &:after {
+      content: '鍒涘缓缃戝叧';
+      position: absolute;
+      left: 100px;
+      width: 90px;
+      font-size: 15px;
+      font-weight: bold;
+      color: #3a84de;
+      border-radius: 2px;
+      border: 1px solid #cccccc;
+      background-color: #fafafa;
+      opacity: 0.8;
+    }
+  }
+  .bpmn-icon-gateway-parallel:hover {
+    &:after {
+      content: '鍒涘缓骞惰缃戝叧';
+      position: absolute;
+      left: 45px;
+      width: 120px;
+      font-size: 15px;
+      font-weight: bold;
+      color: #3a84de;
+      border-radius: 2px;
+      border: 1px solid #cccccc;
+      background-color: #fafafa;
+      opacity: 0.8;
+    }
+  }
+  .bpmn-icon-gateway-eventbased:hover {
+    &:after {
+      content: '鍒涘缓浜嬩欢缃戝叧';
+      position: absolute;
+      left: 100px;
+      width: 120px;
+      font-size: 15px;
+      font-weight: bold;
+      color: #3a84de;
+      border-radius: 2px;
+      border: 1px solid #cccccc;
+      background-color: #fafafa;
+      opacity: 0.8;
+    }
+  }
+  .bpmn-icon-task:hover {
+    &:after {
+      content: '鍒涘缓浠诲姟';
+      position: absolute;
+      left: 45px;
+      width: 80px;
+      font-size: 15px;
+      font-weight: bold;
+      color: #3a84de;
+      border-radius: 2px;
+      border: 1px solid #cccccc;
+      background-color: #fafafa;
+      opacity: 0.8;
+    }
+  }
+  .bpmn-icon-subprocess-expanded:hover {
+    &:after {
+      content: '鍒涘缓鍙姌鍙犲瓙娴佺▼';
+      position: absolute;
+      left: 100px;
+      width: 140px;
+      font-size: 15px;
+      font-weight: bold;
+      color: #3a84de;
+      border-radius: 2px;
+      border: 1px solid #cccccc;
+      background-color: #fafafa;
+      opacity: 0.8;
+    }
+  }
+  .bpmn-icon-user-task:hover {
+    &:after {
+      content: '鍒涘缓鐢ㄦ埛浠诲姟';
+      position: absolute;
+      left: 45px;
+      width: 120px;
+      font-size: 15px;
+      font-weight: bold;
+      color: #3a84de;
+      border-radius: 2px;
+      border: 1px solid #cccccc;
+      background-color: #fafafa;
+      opacity: 0.8;
+    }
+  }
+
+  .task-multi-instance:hover {
+    &:after {
+      content: '鍒涘缓澶氬疄渚嬬敤鎴蜂换鍔�';
+      position: absolute;
+      left: 100px;
+      width: 160px;
+      font-size: 15px;
+      font-weight: bold;
+      color: #3a84de;
+      border-radius: 2px;
+      border: 1px solid #cccccc;
+      background-color: #fafafa;
+      opacity: 0.8;
+    }
+  }
+  .bpmn-icon-participant:hover {
+    &:after {
+      content: '鍒涘缓娉虫睜/娉抽亾';
+      position: absolute;
+      left: 45px;
+      width: 120px;
+      font-size: 15px;
+      font-weight: bold;
+      color: #3a84de;
+      border-radius: 2px;
+      border: 1px solid #cccccc;
+      background-color: #fafafa;
+      opacity: 0.8;
+    }
+  }
+  .bpmn-icon-data-object {
+    display: none;
+    &:hover {
+      &:after {
+        content: '鍒涘缓鏁版嵁瀵硅薄';
+        position: absolute;
+        left: 45px;
+        width: 120px;
+        font-size: 15px;
+        font-weight: bold;
+        color: #3a84de;
+        border-radius: 2px;
+        border: 1px solid #cccccc;
+        background-color: #fafafa;
+        opacity: 0.8;
+      }
+    }
+  }
+  .bpmn-icon-data-store {
+    display: none;
+    &:hover {
+      &:after {
+        content: '鍒涘缓鏁版嵁瀛樺偍';
+        position: absolute;
+        left: 100px;
+        width: 120px;
+        font-size: 15px;
+        font-weight: bold;
+        color: #3a84de;
+        border-radius: 2px;
+        border: 1px solid #cccccc;
+        background-color: #fafafa;
+        opacity: 0.8;
+      }
+    }
+  }
+  .bpmn-icon-group {
+    display: none;
+    &:hover {
+      &:after {
+        content: '鍒涘缓鍒嗙粍';
+        position: absolute;
+        left: 100px;
+        width: 100px;
+        font-size: 15px;
+        font-weight: bold;
+        color: #3a84de;
+        border-radius: 2px;
+        border: 1px solid #cccccc;
+        background-color: #fafafa;
+        opacity: 0.8;
+      }
+    }
+  }
+}
diff --git a/src/bpmn/hooks/usePanel.ts b/src/bpmn/hooks/usePanel.ts
new file mode 100644
index 0000000..d92b7d7
--- /dev/null
+++ b/src/bpmn/hooks/usePanel.ts
@@ -0,0 +1,145 @@
+import showConfig from '../assets/showConfig';
+import { ModdleElement } from 'bpmn';
+import useModelerStore from '@/store/modules/modeler';
+import { MultiInstanceTypeEnum } from '@/enums/bpmn/IndexEnums';
+interface Options {
+  element: ModdleElement;
+}
+
+export default (ops: Options) => {
+  const { element } = ops;
+  const { getModeling, getModdle } = useModelerStore();
+  const modeling = getModeling();
+  const moddle = getModdle();
+
+  /**
+   * 褰撳墠鑺傜偣绫诲瀷
+   */
+  const elementType = computed(() => {
+    const bizObj = element.businessObject;
+    return bizObj.eventDefinitions ? bizObj.eventDefinitions[0].$type : bizObj.$type;
+  });
+
+  /**
+   * 鐢ㄤ簬鎺у埗闈㈡澘瀛楁鏄剧ず涓庨殣钘忕殑閰嶇疆
+   */
+  const config = computed(() => showConfig[elementType.value] || {});
+
+  /**
+   * 鍒涘缓涓�涓妭鐐�
+   * @param elementType 鑺傜偣绫诲瀷
+   * @param properties 灞炴��
+   * @param parent 鐖惰妭鐐�
+   */
+  const createModdleElement = (elementType: string, properties: any, parent: ModdleElement) => {
+    const element = moddle.create(elementType, properties);
+    parent && (element.$parent = parent);
+    return element;
+  };
+
+  /**
+   * 鑾峰彇鎵╁睍灞炴�э紝濡傛灉涓嶅瓨鍦ㄤ細鑷姩鍒涘缓
+   */
+  const getExtensionElements = (create = true) => {
+    let extensionElements = element.businessObject.get<ModdleElement>('extensionElements');
+    if (!extensionElements && create) {
+      extensionElements = createModdleElement('bpmn:ExtensionElements', { values: [] }, element.businessObject);
+      modeling.updateModdleProperties(element, element.businessObject, { extensionElements });
+    }
+    return extensionElements;
+  };
+
+  /**
+   * 鑾峰彇extensionElements涓嬬殑properties
+   * @param extensionElements 鍙�夊弬鏁帮紝榛樿鑾峰彇褰撳墠Element涓嬬殑extensionElements涓嬬殑Properties
+   */
+  const getPropertiesElements = (extensionElements?: ModdleElement) => {
+    if (!extensionElements) {
+      extensionElements = getExtensionElements();
+    }
+    let propertiesElements = extensionElements.values.find((item) => item.$type === 'flowable:properties');
+    if (!propertiesElements) {
+      propertiesElements = createModdleElement('flowable:properties', { values: [] }, extensionElements);
+      modeling.updateModdleProperties(element, extensionElements, {
+        values: [...extensionElements.get<[]>('values'), propertiesElements]
+      });
+    }
+    return propertiesElements;
+  };
+
+  /**
+   * 鏇存柊鑺傜偣灞炴��
+   * @param properties 灞炴�у��
+   */
+  const updateProperties = (properties: any) => {
+    modeling.updateProperties(element, properties);
+  };
+
+  /**
+   * 鏇存柊鑺傜偣淇℃伅
+   * @param updateElement 闇�瑕佹洿鏂扮殑鑺傜偣
+   * @param properties 灞炴��
+   */
+  const updateModdleProperties = (updateElement, properties: any) => {
+    modeling.updateModdleProperties(element, updateElement, properties);
+  };
+
+  /**
+   * 鏇存柊Property灞炴��
+   * @param name key鍊�
+   * @param value 鍊�
+   */
+  const updateProperty = (name: string, value: string) => {
+    const propertiesElements = getPropertiesElements();
+
+    let propertyElements = propertiesElements.values.find((item) => item.name === name);
+    if (!propertyElements) {
+      propertyElements = createModdleElement('flowable:property', { name: name, value: value }, propertiesElements);
+      modeling.updateModdleProperties(element, propertiesElements, {
+        values: [...propertiesElements.get('values'), propertyElements]
+      });
+    } else {
+      propertyElements.name = name;
+      propertyElements.value = value;
+    }
+    return propertyElements;
+  };
+
+  const idChange = (newVal: string) => {
+    if (newVal) {
+      updateProperties({ id: newVal });
+    }
+  };
+  const nameChange = (newVal: string) => {
+    if (newVal) {
+      updateProperties({ name: newVal });
+    }
+  };
+  const formKeyChange = (newVal: string) => {
+    updateProperties({ formKey: newVal });
+  };
+  const constant = {
+    MultiInstanceType: [
+      { id: '373d4b81-a0d1-4eb8-8685-0d2fb1b468e2', label: '鏃�', value: MultiInstanceTypeEnum.NONE },
+      { id: 'b5acea7c-b7e5-46b0-8778-390db091bdab', label: '涓茶', value: MultiInstanceTypeEnum.SERIAL },
+      { id: 'b4f0c683-1ccc-43c4-8380-e1b998986caf', label: '骞惰', value: MultiInstanceTypeEnum.PARALLEL }
+    ]
+  };
+
+  return {
+    elementType,
+    constant,
+    showConfig: config,
+
+    updateProperties,
+    updateProperty,
+    updateModdleProperties,
+
+    createModdleElement,
+    idChange,
+    nameChange,
+    formKeyChange,
+    getExtensionElements,
+    getPropertiesElements
+  };
+};
diff --git a/src/bpmn/hooks/useParseElement.ts b/src/bpmn/hooks/useParseElement.ts
new file mode 100644
index 0000000..a5a255d
--- /dev/null
+++ b/src/bpmn/hooks/useParseElement.ts
@@ -0,0 +1,34 @@
+import { ModdleElement } from 'bpmn';
+
+interface Options {
+  element: ModdleElement;
+}
+
+interface Data {
+  id: string;
+}
+
+export default (ops: Options) => {
+  const { element } = ops;
+
+  const parseData = <T>(): T => {
+    const result = {
+      ...element.businessObject,
+      ...element.businessObject.$attrs
+    };
+
+    // 绉婚櫎flowable鍓嶇紑锛屾牸寮忓寲鏁扮粍
+    for (const key in result) {
+      if (key.indexOf('flowable:') === 0) {
+        const newKey = key.replace('flowable:', '');
+        result[newKey] = result[key];
+        delete result[key];
+      }
+    }
+    return { ...result } as T;
+  };
+
+  return {
+    parseData
+  };
+};
diff --git a/src/bpmn/index.vue b/src/bpmn/index.vue
new file mode 100644
index 0000000..924a838
--- /dev/null
+++ b/src/bpmn/index.vue
@@ -0,0 +1,496 @@
+<template>
+  <div class="containers-bpmn">
+    <!-- dark妯″紡涓� 杩炴帴绾跨殑绠ご鏍峰紡 -->
+    <svg width="0" height="0" style="position: absolute">
+      <defs>
+        <marker id="markerArrow-dark-mode" viewBox="0 0 20 20" refX="11" refY="10" markerWidth="10" markerHeight="10" orient="auto">
+          <path d="M 1 5 L 11 10 L 1 15 Z" class="arrow-dark" />
+        </marker>
+      </defs>
+    </svg>
+    <div v-loading="loading" class="app-containers-bpmn">
+      <el-container class="h-full">
+        <el-container style="align-items: stretch">
+          <el-header>
+            <div class="process-toolbar">
+              <el-space wrap :size="10">
+                <el-tooltip effect="dark" content="鑷�傚簲灞忓箷" placement="bottom">
+                  <el-button size="small" icon="Rank" @click="fitViewport" />
+                </el-tooltip>
+                <el-tooltip effect="dark" content="鏀惧ぇ" placement="bottom">
+                  <el-button size="small" icon="ZoomIn" @click="zoomViewport(true)" />
+                </el-tooltip>
+                <el-tooltip effect="dark" content="缂╁皬" placement="bottom">
+                  <el-button size="small" icon="ZoomOut" @click="zoomViewport(false)" />
+                </el-tooltip>
+                <el-tooltip effect="dark" content="鍚庨��" placement="bottom">
+                  <el-button size="small" icon="Back" @click="bpmnModeler.get('commandStack').undo()" />
+                </el-tooltip>
+                <el-tooltip effect="dark" content="鍓嶈繘" placement="bottom">
+                  <el-button size="small" icon="Right" @click="bpmnModeler.get('commandStack').redo()" />
+                </el-tooltip>
+              </el-space>
+              <el-space wrap :size="10" style="float: right; padding-right: 10px">
+                <el-button size="small" type="primary" @click="saveXml">淇� 瀛�</el-button>
+                <el-dropdown size="small">
+                  <el-button size="small" type="primary"> 棰� 瑙� </el-button>
+                  <template #dropdown>
+                    <el-dropdown-menu>
+                      <el-dropdown-item icon="Document" @click="previewXML">XML棰勮</el-dropdown-item>
+                      <el-dropdown-item icon="View" @click="previewSVG"> SVG棰勮</el-dropdown-item>
+                    </el-dropdown-menu>
+                  </template>
+                </el-dropdown>
+                <el-dropdown size="small">
+                  <el-button size="small" type="primary"> 涓� 杞� </el-button>
+                  <template #dropdown>
+                    <el-dropdown-menu>
+                      <el-dropdown-item icon="Download" @click="downloadXML">涓嬭浇XML</el-dropdown-item>
+                      <el-dropdown-item icon="Download" @click="downloadSVG"> 涓嬭浇SVG</el-dropdown-item>
+                    </el-dropdown-menu>
+                  </template>
+                </el-dropdown>
+              </el-space>
+            </div>
+          </el-header>
+          <div ref="canvas" class="canvas" />
+        </el-container>
+        <div :class="{ 'process-panel': true, 'hide': panelFlag }">
+          <div class="process-panel-bar" @click="panelBarClick">
+            <div class="open-bar">
+              <el-link type="default" :underline="false">
+                <svg-icon class-name="open-bar" :icon-class="panelFlag ? 'caret-back' : 'caret-forward'"></svg-icon>
+              </el-link>
+            </div>
+          </div>
+          <transition enter-active-class="animate__animated animate__fadeIn">
+            <div v-show="showPanel" v-if="bpmnModeler" class="panel-content">
+              <PropertyPanel :modeler="bpmnModeler" />
+            </div>
+          </transition>
+        </div>
+      </el-container>
+    </div>
+  </div>
+  <div>
+    <el-dialog v-model="perviewXMLShow" title="XML棰勮" width="80%" append-to-body>
+      <highlightjs :code="xmlStr" language="XML" />
+    </el-dialog>
+  </div>
+  <div>
+    <el-dialog v-model="perviewSVGShow" title="SVG棰勮" width="80%" append-to-body>
+      <div style="text-align: center" v-html="svgData" />
+    </el-dialog>
+  </div>
+</template>
+
+<script lang="ts" setup name="BpmnDesign">
+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';
+import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css';
+import './assets/style/index.scss';
+import { Canvas, Modeler } from 'bpmn';
+import PropertyPanel from './panel/index.vue';
+import BpmnModeler from 'bpmn-js/lib/Modeler.js';
+import defaultXML from './assets/defaultXML';
+import flowableModdle from './assets/moddle/flowable';
+import Modules from './assets/module/index';
+import useModelerStore from '@/store/modules/modeler';
+import useDialog from '@/hooks/useDialog';
+
+const emit = defineEmits(['closeCallBack', 'saveCallBack']);
+
+const { visible, title, openDialog, closeDialog } = useDialog({
+  title: '缂栬緫娴佺▼'
+});
+const modelerStore = useModelerStore();
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const panelFlag = ref(false);
+const showPanel = ref(true);
+const canvas = ref<HTMLDivElement>();
+const panel = ref<HTMLDivElement>();
+const bpmnModeler = ref<Modeler>();
+const zoom = ref(1);
+const perviewXMLShow = ref(false);
+const perviewSVGShow = ref(false);
+const xmlStr = ref('');
+const svgData = ref('');
+const loading = ref(false);
+
+const panelBarClick = () => {
+  // 寤惰繜鎵ц锛屽惁鍒欎細瀵艰嚧闈㈡澘鏀惰捣鏃讹紝灞炴�ч潰鏉夸笉鏄剧ず
+  panelFlag.value = !panelFlag.value;
+  setTimeout(() => {
+    showPanel.value = !panelFlag.value;
+  }, 100);
+};
+
+/**
+ * 鍒濆鍖朇anvas
+ */
+const initCanvas = () => {
+  bpmnModeler.value = new BpmnModeler({
+    container: canvas.value,
+    // 閿洏
+    keyboard: {
+      bindTo: window // 鎴栬�厀indow锛屾敞鎰忎笌澶栭儴琛ㄥ崟鐨勯敭鐩樼洃鍚簨浠舵槸鍚﹀啿绐�
+    },
+    propertiesPanel: {
+      parent: panel.value
+    },
+    additionalModules: Modules,
+    moddleExtensions: {
+      flowable: flowableModdle
+    }
+  });
+};
+
+/**
+ * 鍒濆鍖朚odel
+ */
+const initModel = () => {
+  if (modelerStore.getModeler()) {
+    modelerStore.getModeler().destroy();
+    modelerStore.setModeler(undefined);
+  }
+  modelerStore.setModeler(bpmnModeler.value);
+};
+
+/**
+ * 鏂板缓
+ */
+const newDiagram = async () => {
+  await proxy?.$modal.confirm('鏄惁纭鏂板缓');
+  initDiagram();
+};
+
+/**
+ * 鍒濆鍖�
+ */
+const initDiagram = (xml?: string) => {
+  if (!xml) xml = defaultXML;
+  bpmnModeler.value.importXML(xml);
+};
+
+/**
+ * 鑷�傚簲灞忓箷
+ */
+const fitViewport = () => {
+  zoom.value = bpmnModeler.value.get<Canvas>('canvas').zoom('fit-viewport');
+  const bbox = document.querySelector<SVGGElement>('.app-containers-bpmn .viewport').getBBox();
+  const currentViewBox = bpmnModeler.value.get<Canvas>('canvas').viewbox();
+  const elementMid = {
+    x: bbox.x + bbox.width / 2 - 65,
+    y: bbox.y + bbox.height / 2
+  };
+  bpmnModeler.value.get<Canvas>('canvas').viewbox({
+    x: elementMid.x - currentViewBox.width / 2,
+    y: elementMid.y - currentViewBox.height / 2,
+    width: currentViewBox.width,
+    height: currentViewBox.height
+  });
+  zoom.value = (bbox.width / currentViewBox.width) * 1.8;
+};
+/**
+ * 鏀惧ぇ鎴栬�呯缉灏�
+ * @param zoomIn true 鏀惧ぇ | false 缂╁皬
+ */
+const zoomViewport = (zoomIn = true) => {
+  zoom.value = bpmnModeler.value.get<Canvas>('canvas').zoom();
+  zoom.value += zoomIn ? 0.1 : -0.1;
+  bpmnModeler.value.get<Canvas>('canvas').zoom(zoom.value);
+};
+
+/**
+ * 涓嬭浇XML
+ */
+const downloadXML = async () => {
+  try {
+    const { xml } = await bpmnModeler.value.saveXML({ format: true });
+    downloadFile(`${getProcessElement().name}.bpmn20.xml`, xml, 'application/xml');
+  } catch (e) {
+    proxy?.$modal.msgError(e);
+  }
+};
+
+/**
+ * 涓嬭浇SVG
+ */
+const downloadSVG = async () => {
+  try {
+    const { svg } = await bpmnModeler.value.saveSVG();
+    downloadFile(getProcessElement().name, svg, 'image/svg+xml');
+  } catch (e) {
+    proxy?.$modal.msgError(e);
+  }
+};
+
+/**
+ * XML棰勮
+ */
+const previewXML = async () => {
+  try {
+    const { xml } = await bpmnModeler.value.saveXML({ format: true });
+    xmlStr.value = xml;
+    perviewXMLShow.value = true;
+  } catch (e) {
+    proxy?.$modal.msgError(e);
+  }
+};
+
+/**
+ * SVG棰勮
+ */
+const previewSVG = async () => {
+  try {
+    const { svg } = await bpmnModeler.value.saveSVG();
+    svgData.value = svg;
+    perviewSVGShow.value = true;
+  } catch (e) {
+    proxy?.$modal.msgError(e);
+  }
+};
+
+const curNodeInfo = reactive({
+  curType: '', // 浠诲姟绫诲瀷 鐢ㄦ埛浠诲姟
+  curNode: '',
+  expValue: '' //澶氱敤鎴峰拰閮ㄩ棬瑙掕壊瀹炵幇
+});
+
+const downloadFile = (fileName: string, data: any, type: string) => {
+  const a = document.createElement('a');
+  const url = window.URL.createObjectURL(new Blob([data], { type: type }));
+  a.href = url;
+  a.download = fileName;
+  a.click();
+  window.URL.revokeObjectURL(url);
+};
+
+const getProcessElement = () => {
+  const rootElements = bpmnModeler.value?.getDefinitions().rootElements;
+  for (let i = 0; i < rootElements.length; i++) {
+    if (rootElements[i].$type === 'bpmn:Process') return rootElements[i];
+  }
+};
+
+const getProcess = () => {
+  const element = getProcessElement();
+  return {
+    id: element.id,
+    name: element.name
+  };
+};
+
+const saveXml = async () => {
+  const { xml } = await bpmnModeler.value.saveXML({ format: true });
+  const { svg } = await bpmnModeler.value.saveSVG();
+  const process = getProcess();
+  let data = {
+    xml: xml,
+    svg: svg,
+    key: process.id,
+    name: process.name,
+    loading: loading
+  };
+  emit('saveCallBack', data);
+};
+
+const open = (xml?: string) => {
+  openDialog();
+  nextTick(() => {
+    initDiagram(xml);
+  });
+};
+const close = () => {
+  closeDialog();
+};
+
+onMounted(() => {
+  nextTick(() => {
+    initCanvas();
+    initModel();
+  });
+});
+
+/**
+ * 瀵瑰鏆撮湶瀛愮粍浠舵柟娉�
+ */
+defineExpose({
+  initDiagram,
+  saveXml,
+  open,
+  close
+});
+</script>
+
+<style lang="scss">
+/** 澶滈棿妯″紡 绾挎潯鐨勯鑹� */
+$stroke-color-dark: white;
+$bpmn-font-size: 12px;
+/** 鏃ラ棿妯″紡 瀛椾綋棰滆壊 */
+$bpmn-font-color-dark: white;
+/** 澶滈棿妯″紡 瀛椾綋棰滆壊 */
+$bpmn-font-color-light: #222;
+
+/* 鑳屾櫙缃戞牸 */
+@mixin djs-container {
+  background-image: linear-gradient(90deg, hsl(0deg 0% 78.4% / 15%) 10%, transparent 0), linear-gradient(hsl(0deg 0% 78.4% / 15%) 10%, transparent 0) !important;
+  background-size: 10px 10px !important;
+}
+
+html[class='light'] {
+  /** 浠庡乏渚ф嫋鍔ㄦ椂鐨勮儗鏅浘 */
+  svg.new-parent {
+    @include djs-container;
+  }
+
+  /** 鍙屽嚮缂栬緫鍏冪礌鏃舵牱寮忎繚鎸佷竴鑷� */
+  div.djs-direct-editing-parent {
+    border-radius: 10px;
+    background-color: transparent !important;
+    color: $bpmn-font-color-light;
+  }
+
+  g.djs-visual {
+    .djs-label {
+      fill: $bpmn-font-color-light !important;
+      font-size: $bpmn-font-size !important;
+    }
+  }
+}
+
+html[class='dark'] {
+  /** dark妯″紡涓� 杩炴帴绾跨殑绠ご鏍峰紡 */
+  .arrow-dark {
+    stroke-width: 1px;
+    stroke-linecap: round;
+    stroke: $stroke-color-dark;
+    fill: $stroke-color-dark;
+    stroke-linejoin: round;
+  }
+
+  /** 浠庡乏渚ф嫋鍔ㄦ椂鐨勮儗鏅浘 */
+  svg.new-parent {
+    background-color: black !important;
+    @include djs-container;
+  }
+
+  /** 鍙屽嚮缂栬緫鍏冪礌鏃舵牱寮忎繚鎸佷竴鑷� */
+  div.djs-direct-editing-parent {
+    border-radius: 10px;
+    background-color: transparent !important;
+    color: $bpmn-font-color-dark;
+  }
+
+  /** 鍏冪礌鐩稿叧璁剧疆 */
+  g.djs-visual {
+    /** 鍏冪礌杈规 闇�瑕佸幓闄ゆ枃瀛�(.djs-label) */
+    & > *:first-child:not(.djs-label) {
+      stroke: $stroke-color-dark !important;
+    }
+
+    /** 瀛椾綋棰滆壊 */
+    .djs-label {
+      fill: $bpmn-font-color-dark !important;
+      font-size: $bpmn-font-size !important;
+    }
+
+    /* 杩炴帴绾挎牱寮� */
+    path[data-corner-radius] {
+      stroke: $stroke-color-dark !important;
+      marker-end: url('#markerArrow-dark-mode') !important;
+    }
+  }
+}
+
+.containers-bpmn {
+  height: 100%;
+  .app-containers-bpmn {
+    width: 100%;
+    height: 100%;
+    .canvas {
+      width: 100%;
+      height: 100%;
+      @include djs-container;
+    }
+    .el-header {
+      height: 35px;
+      padding: 0;
+    }
+
+    .process-panel {
+      transition: width 0.25s ease-in;
+      .process-panel-bar {
+        width: 34px;
+        height: 40px;
+        .open-bar {
+          width: 34px;
+          line-height: 40px;
+        }
+      }
+      // 鏀惰捣闈㈡澘鏍峰紡
+      &.hide {
+        width: 34px;
+        overflow: hidden;
+        padding: 0;
+        .process-panel-bar {
+          width: 34px;
+          height: 100%;
+          box-sizing: border-box;
+          display: block;
+          text-align: left;
+          line-height: 34px;
+        }
+        .process-panel-bar:hover {
+          background-color: var(--bpmn-panel-bar-background-color);
+        }
+      }
+    }
+  }
+}
+pre {
+  margin: 0;
+  height: 100%;
+  max-height: calc(80vh - 32px);
+  overflow-x: hidden;
+  overflow-y: auto;
+  .hljs {
+    word-break: break-word;
+    white-space: pre-wrap;
+    padding: 0.5em;
+  }
+}
+
+.open-bar {
+  font-size: 20px;
+  cursor: pointer;
+  text-align: center;
+}
+.process-panel {
+  box-sizing: border-box;
+  padding: 0 8px 0 8px;
+  border-left: 1px solid var(--bpmn-panel-border);
+  box-shadow: var(--bpmn-panel-box-shadow) 0 0 8px;
+  max-height: 100%;
+  width: 25%;
+  height: calc(100vh - 100px);
+  .el-collapse {
+    height: calc(100vh - 182px);
+    overflow: auto;
+  }
+}
+
+// 浠诲姟鏍� 閫忔槑搴�
+//:deep(.djs-palette) {
+//  opacity: 0.3;
+//  transition: all 1s;
+//}
+//
+//:deep(.djs-palette:hover) {
+//  opacity: 1;
+//  transition: all 1s;
+//}
+</style>
diff --git a/src/bpmn/panel/GatewayPanel.vue b/src/bpmn/panel/GatewayPanel.vue
new file mode 100644
index 0000000..5931539
--- /dev/null
+++ b/src/bpmn/panel/GatewayPanel.vue
@@ -0,0 +1,68 @@
+<template>
+  <div>
+    <el-collapse v-model="currentCollapseItem">
+      <el-collapse-item name="1">
+        <template #title>
+          <div class="collapse__title">
+            <el-icon>
+              <InfoFilled />
+            </el-icon>
+            甯歌
+          </div>
+        </template>
+        <div>
+          <el-form ref="formRef" :model="formData" :rules="formRules" label-width="80px">
+            <el-form-item prop="id" label="鑺傜偣 ID">
+              <el-input v-model="formData.id" @change="idChange"> </el-input>
+            </el-form-item>
+            <el-form-item prop="name" label="鑺傜偣鍚嶇О">
+              <el-input v-model="formData.name" @change="nameChange"> </el-input>
+            </el-form-item>
+          </el-form>
+        </div>
+      </el-collapse-item>
+
+      <el-collapse-item name="2">
+        <template #title>
+          <div class="collapse__title">
+            <el-icon>
+              <BellFilled />
+            </el-icon>
+            鎵ц鐩戝惉鍣�
+          </div>
+        </template>
+        <div>
+          <ExecutionListener :element="element"></ExecutionListener>
+        </div>
+      </el-collapse-item>
+    </el-collapse>
+  </div>
+</template>
+<script setup lang="ts">
+import useParseElement from '../hooks/useParseElement';
+import usePanel from '../hooks/usePanel';
+import { Modeler, ModdleElement } from 'bpmn';
+import { GatewayPanel } from 'bpmnDesign';
+import ExecutionListener from './property/ExecutionListener.vue';
+
+interface PropType {
+  element: ModdleElement;
+}
+const props = withDefaults(defineProps<PropType>(), {});
+const { nameChange, idChange } = usePanel({
+  element: toRaw(props.element)
+});
+const { parseData } = useParseElement({
+  element: toRaw(props.element)
+});
+const currentCollapseItem = ref(['1', '2']);
+const formData = ref(parseData<GatewayPanel>());
+
+const formRules = ref<ElFormRules>({
+  processCategory: [{ required: true, message: '璇烽�夋嫨', trigger: 'blur' }],
+  id: [{ required: true, message: '璇疯緭鍏�', trigger: 'blur' }],
+  name: [{ required: true, message: '璇疯緭鍏�', trigger: 'blur' }]
+});
+</script>
+
+<style lang="scss" scoped></style>
diff --git a/src/bpmn/panel/ParticipantPanel.vue b/src/bpmn/panel/ParticipantPanel.vue
new file mode 100644
index 0000000..cca28bd
--- /dev/null
+++ b/src/bpmn/panel/ParticipantPanel.vue
@@ -0,0 +1,68 @@
+<template>
+  <div>
+    <el-collapse v-model="currentCollapseItem">
+      <el-collapse-item name="1">
+        <template #title>
+          <div class="collapse__title">
+            <el-icon>
+              <InfoFilled />
+            </el-icon>
+            甯歌
+          </div>
+        </template>
+        <div>
+          <el-form ref="formRef" :model="formData" :rules="formRules" label-width="90px">
+            <el-form-item prop="id" label="鑺傜偣 ID">
+              <el-input v-model="formData.id" @change="idChange"></el-input>
+            </el-form-item>
+            <el-form-item prop="name" label="鑺傜偣鍚嶇О">
+              <el-input v-model="formData.name" @change="nameChange"></el-input>
+            </el-form-item>
+          </el-form>
+        </div>
+      </el-collapse-item>
+
+      <el-collapse-item name="2">
+        <template #title>
+          <div class="collapse__title">
+            <el-icon>
+              <BellFilled />
+            </el-icon>
+            鎵ц鐩戝惉鍣�
+          </div>
+        </template>
+        <div>
+          <ExecutionListener :element="element"></ExecutionListener>
+        </div>
+      </el-collapse-item>
+    </el-collapse>
+  </div>
+</template>
+<script setup lang="ts">
+import useParseElement from '../hooks/useParseElement';
+import usePanel from '../hooks/usePanel';
+import ExecutionListener from './property/ExecutionListener.vue';
+import { ModdleElement } from 'bpmn';
+import { ParticipantPanel } from 'bpmnDesign';
+
+interface PropType {
+  element: ModdleElement;
+}
+
+const props = withDefaults(defineProps<PropType>(), {});
+const { nameChange, idChange } = usePanel({
+  element: toRaw(props.element)
+});
+const { parseData } = useParseElement({
+  element: toRaw(props.element)
+});
+
+const formData = ref(parseData<ParticipantPanel>());
+const currentCollapseItem = ref(['1', '2']);
+const formRules = ref<ElFormRules>({
+  id: [{ required: true, message: '璇疯緭鍏�', trigger: 'blur' }],
+  name: [{ required: true, message: '璇疯緭鍏�', trigger: 'blur' }]
+});
+</script>
+
+<style lang="scss" scoped></style>
diff --git a/src/bpmn/panel/ProcessPanel.vue b/src/bpmn/panel/ProcessPanel.vue
new file mode 100644
index 0000000..7d9a359
--- /dev/null
+++ b/src/bpmn/panel/ProcessPanel.vue
@@ -0,0 +1,71 @@
+<template>
+  <div>
+    <el-collapse v-model="currentCollapseItem">
+      <el-collapse-item name="1">
+        <template #title>
+          <div class="collapse__title">
+            <el-icon>
+              <InfoFilled />
+            </el-icon>
+            甯歌
+          </div>
+        </template>
+        <div>
+          <el-form ref="formRef" :model="formData" :rules="formRules" label-width="80px">
+            <el-form-item label="娴佺▼鏍囪瘑" prop="id">
+              <el-input v-model="formData.id" @change="idChange"></el-input>
+            </el-form-item>
+            <el-form-item label="娴佺▼鍚嶇О" prop="name">
+              <el-input v-model="formData.name" @change="nameChange"></el-input>
+            </el-form-item>
+          </el-form>
+        </div>
+      </el-collapse-item>
+
+      <el-collapse-item name="2">
+        <template #title>
+          <div class="collapse__title">
+            <el-icon>
+              <BellFilled />
+            </el-icon>
+            鎵ц鐩戝惉鍣�
+          </div>
+        </template>
+        <div>
+          <ExecutionListener :element="element"></ExecutionListener>
+        </div>
+      </el-collapse-item>
+    </el-collapse>
+  </div>
+</template>
+
+<script setup lang="ts">
+import ExecutionListener from './property/ExecutionListener.vue';
+import useParseElement from '../hooks/useParseElement';
+import usePanel from '../hooks/usePanel';
+import { Modeler, ModdleElement } from 'bpmn';
+import { ProcessPanel } from 'bpmnDesign';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+interface PropType {
+  element: ModdleElement;
+}
+const props = withDefaults(defineProps<PropType>(), {});
+
+const { parseData } = useParseElement({
+  element: toRaw(props.element)
+});
+const { idChange, nameChange } = usePanel({
+  element: toRaw(props.element)
+});
+const currentCollapseItem = ref(['1', '2']);
+const formData = ref<ProcessPanel>(parseData<ProcessPanel>());
+
+const formRules = ref<ElFormRules>({
+  id: [{ required: true, message: '璇疯緭鍏�', trigger: 'blur' }],
+  name: [{ required: true, message: '璇疯緭鍏�', trigger: 'blur' }]
+});
+</script>
+
+<style scoped lang="scss"></style>
diff --git a/src/bpmn/panel/SequenceFlowPanel.vue b/src/bpmn/panel/SequenceFlowPanel.vue
new file mode 100644
index 0000000..fd8ad3c
--- /dev/null
+++ b/src/bpmn/panel/SequenceFlowPanel.vue
@@ -0,0 +1,95 @@
+<template>
+  <div>
+    <el-collapse v-model="currentCollapseItem">
+      <el-collapse-item name="1">
+        <template #title>
+          <div class="collapse__title">
+            <el-icon>
+              <InfoFilled />
+            </el-icon>
+            甯歌
+          </div>
+        </template>
+        <div>
+          <el-form ref="formRef" :model="formData" :rules="formRules" label-width="90px">
+            <el-form-item prop="id" label="鑺傜偣 ID">
+              <el-input v-model="formData.id" @change="idChange"> </el-input>
+            </el-form-item>
+            <el-form-item prop="name" label="鑺傜偣鍚嶇О">
+              <el-input v-model="formData.name" @change="nameChange"> </el-input>
+            </el-form-item>
+            <el-form-item prop="conditionExpression" label="璺宠浆鏉′欢">
+              <el-input v-model="formData.conditionExpressionValue" @change="conditionExpressionChange"> </el-input>
+            </el-form-item>
+            <el-form-item prop="skipExpression" label="璺宠繃琛ㄨ揪寮�">
+              <el-input v-model="formData.skipExpression" @change="skipExpressionChange"> </el-input>
+            </el-form-item>
+          </el-form>
+        </div>
+      </el-collapse-item>
+
+      <el-collapse-item name="2">
+        <template #title>
+          <div class="collapse__title">
+            <el-icon>
+              <BellFilled />
+            </el-icon>
+            鎵ц鐩戝惉鍣�
+          </div>
+        </template>
+        <div>
+          <ExecutionListener :element="element"></ExecutionListener>
+        </div>
+      </el-collapse-item>
+    </el-collapse>
+  </div>
+</template>
+<script setup lang="ts">
+import useParseElement from '../hooks/useParseElement';
+import useModelerStore from '@/store/modules/modeler';
+import usePanel from '../hooks/usePanel';
+import ExecutionListener from './property/ExecutionListener.vue';
+import { Modeler, ModdleElement } from 'bpmn';
+import { SequenceFlowPanel } from 'bpmnDesign';
+
+interface PropType {
+  element: ModdleElement;
+}
+const props = withDefaults(defineProps<PropType>(), {});
+const { nameChange, idChange, updateProperties } = usePanel({
+  element: toRaw(props.element)
+});
+const { parseData } = useParseElement({
+  element: toRaw(props.element)
+});
+const moddle = useModelerStore().getModdle();
+const currentCollapseItem = ref(['1', '2']);
+const formData = ref(parseData<SequenceFlowPanel>());
+
+const formRules = ref<ElFormRules>({
+  processCategory: [{ required: true, message: '璇烽�夋嫨', trigger: 'blur' }],
+  id: [{ required: true, message: '璇疯緭鍏�', trigger: 'blur' }],
+  name: [{ required: true, message: '璇疯緭鍏�', trigger: 'blur' }]
+});
+
+const conditionExpressionChange = (val: string) => {
+  if (val) {
+    const newCondition = moddle.create('bpmn:FormalExpression', { body: val });
+    updateProperties({ conditionExpression: newCondition });
+  } else {
+    updateProperties({ conditionExpression: null });
+  }
+};
+
+const skipExpressionChange = (val: string) => {
+  updateProperties({ 'flowable:skipExpression': val });
+};
+
+onBeforeMount(() => {
+  if (formData.value.conditionExpression) {
+    formData.value.conditionExpressionValue = formData.value.conditionExpression.body;
+  }
+});
+</script>
+
+<style lang="scss" scoped></style>
diff --git a/src/bpmn/panel/StartEndPanel.vue b/src/bpmn/panel/StartEndPanel.vue
new file mode 100644
index 0000000..d43ed80
--- /dev/null
+++ b/src/bpmn/panel/StartEndPanel.vue
@@ -0,0 +1,67 @@
+<template>
+  <div>
+    <el-collapse v-model="currentCollapseItem">
+      <el-collapse-item name="1">
+        <template #title>
+          <div class="collapse__title">
+            <el-icon>
+              <InfoFilled />
+            </el-icon>
+            甯歌
+          </div>
+        </template>
+        <div>
+          <el-form ref="formRef" :model="formData" :rules="formRules" label-width="90px">
+            <el-form-item prop="id" label="鑺傜偣 ID">
+              <el-input v-model="formData.id" @change="idChange"> </el-input>
+            </el-form-item>
+            <el-form-item prop="name" label="鑺傜偣鍚嶇О">
+              <el-input v-model="formData.name" @change="nameChange"> </el-input>
+            </el-form-item>
+          </el-form>
+        </div>
+      </el-collapse-item>
+
+      <el-collapse-item name="2">
+        <template #title>
+          <div class="collapse__title">
+            <el-icon>
+              <BellFilled />
+            </el-icon>
+            鎵ц鐩戝惉鍣�
+          </div>
+        </template>
+        <div>
+          <ExecutionListener :element="element"></ExecutionListener>
+        </div>
+      </el-collapse-item>
+    </el-collapse>
+  </div>
+</template>
+<script setup lang="ts">
+import ExecutionListener from './property/ExecutionListener.vue';
+import useParseElement from '../hooks/useParseElement';
+import usePanel from '../hooks/usePanel';
+import { Modeler, ModdleElement } from 'bpmn';
+import { StartEndPanel } from 'bpmnDesign';
+
+interface PropType {
+  element: ModdleElement;
+}
+const props = withDefaults(defineProps<PropType>(), {});
+const { nameChange, idChange } = usePanel({
+  element: toRaw(props.element)
+});
+const { parseData } = useParseElement({
+  element: toRaw(props.element)
+});
+
+const formData = ref(parseData<StartEndPanel>());
+const currentCollapseItem = ref(['1', '2']);
+const formRules = ref<ElFormRules>({
+  id: [{ required: true, message: '璇疯緭鍏�', trigger: 'blur' }],
+  name: [{ required: true, message: '璇疯緭鍏�', trigger: 'blur' }]
+});
+</script>
+
+<style lang="scss" scoped></style>
diff --git a/src/bpmn/panel/SubProcessPanel.vue b/src/bpmn/panel/SubProcessPanel.vue
new file mode 100644
index 0000000..e0cedcb
--- /dev/null
+++ b/src/bpmn/panel/SubProcessPanel.vue
@@ -0,0 +1,193 @@
+<template>
+  <div>
+    <el-form ref="formRef" :model="formData" :rules="formRules" label-width="90px">
+      <el-collapse v-model="currentCollapseItem">
+        <el-collapse-item name="1">
+          <template #title>
+            <div class="collapse__title">
+              <el-icon>
+                <InfoFilled />
+              </el-icon>
+              甯歌
+            </div>
+          </template>
+          <div>
+            <el-form-item prop="id" label="鑺傜偣 ID">
+              <el-input v-model="formData.id" @change="idChange"> </el-input>
+            </el-form-item>
+            <el-form-item prop="name" label="鑺傜偣鍚嶇О">
+              <el-input v-model="formData.name" @change="nameChange"> </el-input>
+            </el-form-item>
+          </div>
+        </el-collapse-item>
+
+        <el-collapse-item name="2">
+          <template #title>
+            <div class="collapse__title">
+              <el-icon>
+                <BellFilled />
+              </el-icon>
+              鎵ц鐩戝惉鍣�
+            </div>
+          </template>
+          <div>
+            <ExecutionListener :element="element"></ExecutionListener>
+          </div>
+        </el-collapse-item>
+        <el-collapse-item name="3">
+          <template #title>
+            <div class="collapse__title">
+              <el-icon>
+                <HelpFilled />
+              </el-icon>
+              澶氬疄渚�
+            </div>
+          </template>
+          <div>
+            <el-form-item label="澶氬疄渚嬬被鍨�">
+              <el-select v-model="formData.multiInstanceType" @change="multiInstanceTypeChange">
+                <el-option v-for="item in constant.MultiInstanceType" :key="item.id" :value="item.value" :label="item.label"> </el-option>
+              </el-select>
+            </el-form-item>
+
+            <div v-if="formData.multiInstanceType !== MultiInstanceTypeEnum.NONE">
+              <el-form-item label="闆嗗悎">
+                <template #label>
+                  <span>
+                    闆嗗悎
+                    <el-tooltip placement="top">
+                      <el-icon><QuestionFilled /></el-icon>
+                      <template #content>
+                        灞炴�т細浣滀负琛ㄨ揪寮忚繘琛岃В鏋愩�傚鏋滆〃杈惧紡瑙f瀽涓哄瓧绗︿覆鑰屼笉鏄竴涓泦鍚堬紝<br />
+                        涓嶈鏄洜涓烘湰韬厤缃殑灏辨槸闈欐�佸瓧绗︿覆鍊硷紝杩樻槸琛ㄨ揪寮忚绠楃粨鏋滀负瀛楃涓诧紝<br />
+                        杩欎釜瀛楃涓查兘浼氳褰撳仛鍙橀噺鍚嶏紝骞朵粠娴佺▼鍙橀噺涓敤浜庤幏鍙栧疄闄呯殑闆嗗悎銆�
+                      </template>
+                    </el-tooltip>
+                  </span>
+                </template>
+                <el-input v-model="formData.collection" @change="collectionChange"></el-input>
+              </el-form-item>
+              <el-form-item label="鍏冪礌鍙橀噺">
+                <template #label>
+                  <span>
+                    鍏冪礌鍙橀噺
+                    <el-tooltip placement="top">
+                      <el-icon><QuestionFilled /></el-icon>
+                      <template #content>
+                        姣忓垱寤轰竴涓敤鎴蜂换鍔″墠锛屽厛浠ヨ鍏冪礌鍙橀噺涓簂abel锛岄泦鍚堜腑鐨勪竴椤逛负value锛�<br />
+                        鍒涘缓锛堝眬閮級娴佺▼鍙橀噺锛岃灞�閮ㄦ祦绋嬪彉閲忚鐢ㄤ簬鎸囨淳鐢ㄦ埛浠诲姟銆�<br />
+                        涓�鑸潵璇达紝璇ュ瓧绗︿覆搴斾笌鎸囧畾浜哄憳鍙橀噺鐩稿悓銆�
+                      </template>
+                    </el-tooltip>
+                  </span>
+                </template>
+                <el-input v-model="formData.elementVariable" @change="elementVariableChange"> </el-input>
+              </el-form-item>
+              <el-form-item label="瀹屾垚鏉′欢">
+                <template #label>
+                  <span>
+                    瀹屾垚鏉′欢
+                    <el-tooltip placement="top">
+                      <el-icon><QuestionFilled /></el-icon>
+                      <template #content>
+                        澶氬疄渚嬫椿鍔ㄥ湪鎵�鏈夊疄渚嬮兘瀹屾垚鏃剁粨鏉燂紝鐒惰�屼篃鍙互鎸囧畾涓�涓〃杈惧紡锛屽湪姣忎釜瀹炰緥<br />
+                        缁撴潫鏃惰繘琛岃绠椼�傚綋琛ㄨ揪寮忚绠椾负true鏃讹紝灏嗛攢姣佹墍鏈夊墿浣欑殑瀹炰緥锛屽苟缁撴潫澶氬疄渚�<br />
+                        娲诲姩锛岀户缁墽琛屾祦绋嬨�備緥濡� ${nrOfCompletedInstances/nrOfInstances >= 0.6 }锛�<br />
+                        琛ㄧず褰撲换鍔″畬鎴�60%鏃讹紝璇ヨ妭鐐瑰氨绠楀畬鎴�
+                      </template>
+                    </el-tooltip>
+                  </span>
+                </template>
+                <el-input v-model="formData.completionCondition" @change="completionConditionChange"> </el-input>
+              </el-form-item>
+            </div>
+          </div>
+        </el-collapse-item>
+      </el-collapse>
+    </el-form>
+  </div>
+</template>
+<script setup lang="ts">
+import ExecutionListener from './property/ExecutionListener.vue';
+import useParseElement from '../hooks/useParseElement';
+import usePanel from '../hooks/usePanel';
+import { ModdleElement } from 'bpmn';
+import { SubProcessPanel } from 'bpmnDesign';
+import { MultiInstanceTypeEnum } from '@/enums/bpmn/IndexEnums';
+
+interface PropType {
+  element: ModdleElement;
+}
+const props = withDefaults(defineProps<PropType>(), {});
+const { nameChange, idChange, updateProperties, createModdleElement, constant } = usePanel({
+  element: toRaw(props.element)
+});
+const { parseData } = useParseElement({
+  element: toRaw(props.element)
+});
+
+const formData = ref(parseData<SubProcessPanel>());
+const currentCollapseItem = ref(['1', '2', '3']);
+
+const multiInstanceTypeChange = (newVal) => {
+  if (newVal !== MultiInstanceTypeEnum.NONE) {
+    let loopCharacteristics = props.element.businessObject.get('loopCharacteristics');
+    if (!loopCharacteristics) {
+      loopCharacteristics = createModdleElement('bpmn:MultiInstanceLoopCharacteristics', {}, props.element.businessObject);
+    }
+    loopCharacteristics.isSequential = newVal === MultiInstanceTypeEnum.SERIAL;
+    updateProperties({ loopCharacteristics: loopCharacteristics });
+  } else {
+    updateProperties({ loopCharacteristics: undefined });
+  }
+};
+const collectionChange = (newVal) => {
+  let loopCharacteristics = props.element.businessObject.get('loopCharacteristics');
+  if (!loopCharacteristics) {
+    loopCharacteristics = createModdleElement('bpmn:MultiInstanceLoopCharacteristics', {}, props.element.businessObject);
+  }
+  loopCharacteristics.collection = newVal && newVal.length > 0 ? newVal : undefined;
+  updateProperties({ loopCharacteristics: loopCharacteristics });
+};
+const elementVariableChange = (newVal) => {
+  let loopCharacteristics = props.element.businessObject.get('loopCharacteristics');
+  if (!loopCharacteristics) {
+    loopCharacteristics = createModdleElement('bpmn:MultiInstanceLoopCharacteristics', {}, props.element.businessObject);
+  }
+  loopCharacteristics.elementVariable = newVal && newVal.length > 0 ? newVal : undefined;
+  updateProperties({ loopCharacteristics: loopCharacteristics });
+};
+const completionConditionChange = (newVal) => {
+  let loopCharacteristics = props.element.businessObject.get<ModdleElement>('loopCharacteristics');
+  if (!loopCharacteristics) {
+    loopCharacteristics = createModdleElement('bpmn:MultiInstanceLoopCharacteristics', {}, props.element.businessObject);
+  }
+  if (newVal && newVal.length > 0) {
+    if (!loopCharacteristics.completionCondition) {
+      loopCharacteristics.completionCondition = createModdleElement('bpmn:Expression', { body: newVal }, loopCharacteristics);
+    } else {
+      loopCharacteristics.completionCondition.body = newVal;
+    }
+  } else {
+    loopCharacteristics.completionCondition = undefined;
+  }
+  updateProperties({ loopCharacteristics: loopCharacteristics });
+};
+
+onBeforeMount(() => {
+  if (formData.value.loopCharacteristics) {
+    const loopCharacteristics = formData.value.loopCharacteristics;
+    formData.value.collection = loopCharacteristics.collection || '';
+    formData.value.elementVariable = loopCharacteristics.elementVariable || '';
+    formData.value.completionCondition = loopCharacteristics.completionCondition?.body || '';
+    formData.value.multiInstanceType = loopCharacteristics.isSequential ? MultiInstanceTypeEnum.SERIAL : MultiInstanceTypeEnum.PARALLEL;
+  }
+});
+
+const formRules = ref<ElFormRules>({
+  id: [{ required: true, message: '璇疯緭鍏�', trigger: 'blur' }],
+  name: [{ required: true, message: '璇疯緭鍏�', trigger: 'blur' }]
+});
+</script>
+
+<style lang="scss" scoped></style>
diff --git a/src/bpmn/panel/TaskPanel.vue b/src/bpmn/panel/TaskPanel.vue
new file mode 100644
index 0000000..bc037ee
--- /dev/null
+++ b/src/bpmn/panel/TaskPanel.vue
@@ -0,0 +1,492 @@
+<template>
+  <div>
+    <el-form ref="formRef" size="default" :model="formData" :rules="formRules" label-width="100px">
+      <el-collapse v-model="currentCollapseItem">
+        <el-collapse-item name="1">
+          <template #title>
+            <div class="collapse__title">
+              <el-icon>
+                <InfoFilled />
+              </el-icon>
+              甯歌
+            </div>
+          </template>
+          <div>
+            <el-form-item prop="id" label="鑺傜偣 ID">
+              <el-input v-model="formData.id" @change="idChange"> </el-input>
+            </el-form-item>
+            <el-form-item prop="name" label="鑺傜偣鍚嶇О">
+              <el-input v-model="formData.name" @change="nameChange"> </el-input>
+            </el-form-item>
+            <el-form-item v-if="showConfig.skipExpression" prop="skipExpression" label="璺宠繃琛ㄨ揪寮�">
+              <el-input v-model="formData.skipExpression" @change="skipExpressionChange"> </el-input>
+            </el-form-item>
+            <el-form-item v-loading="formManageListLoading" prop="formKey" label="琛ㄥ崟鍦板潃">
+              <el-select v-model="formData.formKey" clearable filterable placeholder="璇烽�夋嫨琛ㄥ崟" style="width: 260px" @change="formKeyChange">
+                <el-option
+                  v-for="item in formManageList"
+                  :key="item.id"
+                  :label="item.formTypeName + ':' + item.formName"
+                  :value="item.formType + ':' + item.id"
+                />
+              </el-select>
+            </el-form-item>
+          </div>
+        </el-collapse-item>
+        <el-collapse-item name="2">
+          <template #title>
+            <div class="collapse__title">
+              <el-icon>
+                <Checked />
+              </el-icon>
+              浠诲姟
+            </div>
+          </template>
+          <div>
+            <el-form-item v-if="showConfig.async" prop="sync" label="鏄惁寮傛">
+              <el-switch v-model="formData.async" inline-prompt active-text="鏄�" inactive-text="鍚�" @change="syncChange" />
+            </el-form-item>
+
+            <el-tabs tab-position="left" class="demo-tabs">
+              <el-tab-pane label="韬唤瀛樺偍">
+                <el-form-item label="鍒嗛厤浜哄憳">
+                  <el-input v-model="formData.assignee" @blur="blurAssignee(formData.assignee)">
+                    <template #append>
+                      <el-button icon="Search" type="primary" @click="openSingleUserSelect" />
+                    </template>
+                  </el-input>
+                </el-form-item>
+                <el-form-item label="鍊欓�変汉鍛�">
+                  <el-badge :value="selectUserLength" :max="99">
+                    <el-button size="small" type="primary" @click="openUserSelect">閫夋嫨浜哄憳</el-button>
+                  </el-badge>
+                </el-form-item>
+                <el-form-item label="鍊欓�夌粍">
+                  <el-badge :value="selectRoleLength" :max="99">
+                    <el-button size="small" type="primary" @click="openRoleSelect">閫夋嫨缁�</el-button>
+                  </el-badge>
+                </el-form-item>
+              </el-tab-pane>
+
+              <!-- <el-tab-pane label="鍥哄畾鍊�">
+                <el-form-item prop="auditUserType" label="鍒嗛厤绫诲瀷">
+                  <el-select v-model="formData.allocationType">
+                    <el-option v-for="item in AllocationTypeSelect" :key="item.id" :value="item.value" :label="item.label"> </el-option>
+                  </el-select>
+                </el-form-item>
+                <el-form-item v-if="formData.allocationType === AllocationTypeEnum.USER" label="鍒嗛厤浜哄憳">
+                  <el-input v-model="formData.assignee">
+                    <template #append>
+                      <el-button icon="Search" type="primary" @click="openSingleUserSelect" />
+                    </template>
+                  </el-input>
+                </el-form-item>
+                <div v-if="formData.allocationType === AllocationTypeEnum.CANDIDATE">
+                  <el-form-item label="鍊欓�変汉鍛�">
+                    <el-badge :value="selectUserLength" :max="99">
+                      <el-button size="small" type="primary" @click="openUserSelect">閫夋嫨浜哄憳</el-button>
+                    </el-badge>
+                  </el-form-item>
+                  <el-form-item label="鍊欓�夌粍">
+                    <el-badge :value="selectRoleLength" :max="99">
+                      <el-button size="small" type="primary" @click="openRoleSelect">閫夋嫨缁�</el-button>
+                    </el-badge>
+                  </el-form-item>
+                </div>
+                <el-form-item v-if="formData.allocationType === AllocationTypeEnum.SPECIFY && showConfig.specifyDesc" style="">
+                  <el-radio-group v-model="formData.specifyDesc" class="ml-4">
+                    <el-radio v-for="item in SpecifyDesc" :key="item.id" :value="item.value" size="large">{{ item.label }}</el-radio>
+                  </el-radio-group>
+                </el-form-item>
+              </el-tab-pane> -->
+            </el-tabs>
+
+            <el-form-item v-if="showConfig.dueDate" prop="dueDate" label="鍒版湡鏃堕棿">
+              <el-input v-model="formData.dueDate" clearable @change="dueDateChange" @click="openDueDate">
+                <template #append>
+                  <el-button icon="Search" type="primary" @click="openDueDate" />
+                </template>
+              </el-input>
+            </el-form-item>
+            <el-form-item v-if="showConfig.priority" prop="priority" label="浼樺厛绾�">
+              <el-input-number v-model="formData.priority" :min="0" @change="priorityChange"> </el-input-number>
+            </el-form-item>
+          </div>
+        </el-collapse-item>
+        <el-collapse-item name="3">
+          <template #title>
+            <div class="collapse__title">
+              <el-icon>
+                <HelpFilled />
+              </el-icon>
+              澶氬疄渚�
+            </div>
+          </template>
+          <div>
+            <el-form-item label="澶氬疄渚嬬被鍨�">
+              <el-select v-model="formData.multiInstanceType" @change="multiInstanceTypeChange">
+                <el-option v-for="item in constant.MultiInstanceType" :key="item.id" :value="item.value" :label="item.label"> </el-option>
+              </el-select>
+            </el-form-item>
+
+            <div v-if="formData.multiInstanceType !== MultiInstanceTypeEnum.NONE">
+              <el-form-item label="闆嗗悎">
+                <template #label>
+                  <span>
+                    闆嗗悎
+                    <el-tooltip placement="top">
+                      <el-icon><QuestionFilled /></el-icon>
+                      <template #content>
+                        灞炴�т細浣滀负琛ㄨ揪寮忚繘琛岃В鏋愩�傚鏋滆〃杈惧紡瑙f瀽涓哄瓧绗︿覆鑰屼笉鏄竴涓泦鍚堬紝<br />
+                        涓嶈鏄洜涓烘湰韬厤缃殑灏辨槸闈欐�佸瓧绗︿覆鍊硷紝杩樻槸琛ㄨ揪寮忚绠楃粨鏋滀负瀛楃涓诧紝<br />
+                        杩欎釜瀛楃涓查兘浼氳褰撳仛鍙橀噺鍚嶏紝骞朵粠娴佺▼鍙橀噺涓敤浜庤幏鍙栧疄闄呯殑闆嗗悎銆�
+                      </template>
+                    </el-tooltip>
+                  </span>
+                </template>
+                <el-input v-model="formData.collection" @change="collectionChange"></el-input>
+              </el-form-item>
+              <el-form-item label="鍏冪礌鍙橀噺">
+                <template #label>
+                  <span>
+                    鍏冪礌鍙橀噺
+                    <el-tooltip placement="top">
+                      <el-icon><QuestionFilled /></el-icon>
+                      <template #content>
+                        姣忓垱寤轰竴涓敤鎴蜂换鍔″墠锛屽厛浠ヨ鍏冪礌鍙橀噺涓簂abel锛岄泦鍚堜腑鐨勪竴椤逛负value锛�<br />
+                        鍒涘缓锛堝眬閮級娴佺▼鍙橀噺锛岃灞�閮ㄦ祦绋嬪彉閲忚鐢ㄤ簬鎸囨淳鐢ㄦ埛浠诲姟銆�<br />
+                        涓�鑸潵璇达紝璇ュ瓧绗︿覆搴斾笌鎸囧畾浜哄憳鍙橀噺鐩稿悓銆�
+                      </template>
+                    </el-tooltip>
+                  </span>
+                </template>
+                <el-input v-model="formData.elementVariable" @change="elementVariableChange"> </el-input>
+              </el-form-item>
+              <el-form-item label="瀹屾垚鏉′欢">
+                <template #label>
+                  <span>
+                    瀹屾垚鏉′欢
+                    <el-tooltip placement="top">
+                      <el-icon><QuestionFilled /></el-icon>
+                      <template #content>
+                        澶氬疄渚嬫椿鍔ㄥ湪鎵�鏈夊疄渚嬮兘瀹屾垚鏃剁粨鏉燂紝鐒惰�屼篃鍙互鎸囧畾涓�涓〃杈惧紡锛屽湪姣忎釜瀹炰緥<br />
+                        缁撴潫鏃惰繘琛岃绠椼�傚綋琛ㄨ揪寮忚绠椾负true鏃讹紝灏嗛攢姣佹墍鏈夊墿浣欑殑瀹炰緥锛屽苟缁撴潫澶氬疄渚�<br />
+                        娲诲姩锛岀户缁墽琛屾祦绋嬨�備緥濡� ${nrOfCompletedInstances/nrOfInstances >= 0.6 }锛�<br />
+                        琛ㄧず褰撲换鍔″畬鎴�60%鏃讹紝璇ヨ妭鐐瑰氨绠楀畬鎴�
+                      </template>
+                    </el-tooltip>
+                  </span>
+                </template>
+                <el-input v-model="formData.completionCondition" @change="completionConditionChange"> </el-input>
+              </el-form-item>
+            </div>
+          </div>
+        </el-collapse-item>
+        <el-collapse-item v-if="showConfig.taskListener" name="4">
+          <template #title>
+            <div class="collapse__title">
+              <el-icon>
+                <BellFilled />
+              </el-icon>
+              浠诲姟鐩戝惉鍣�
+            </div>
+          </template>
+          <div>
+            <TaskListener v-if="showConfig.taskListener" :element="element"></TaskListener>
+          </div>
+        </el-collapse-item>
+        <el-collapse-item v-if="showConfig.executionListener" name="5">
+          <template #title>
+            <div class="collapse__title">
+              <el-icon>
+                <BellFilled />
+              </el-icon>
+              鎵ц鐩戝惉鍣�
+            </div>
+          </template>
+          <div>
+            <ExecutionListener v-if="showConfig.executionListener" :element="element"></ExecutionListener>
+          </div>
+        </el-collapse-item>
+
+        <el-form-item v-if="showConfig.isForCompensation" prop="isForCompensation" label="鏄惁涓鸿ˉ鍋�">
+          <el-switch v-model="formData.isForCompensation" inline-prompt active-text="鏄�" inactive-text="鍚�" />
+        </el-form-item>
+        <el-form-item v-if="showConfig.triggerServiceTask" prop="triggerServiceTask" label="鏈嶅姟浠诲姟鍙Е鍙�">
+          <el-switch v-model="formData.triggerServiceTask" inline-prompt active-text="鏄�" inactive-text="鍚�" />
+        </el-form-item>
+        <el-form-item v-if="showConfig.autoStoreVariables" prop="autoStoreVariables" label="鑷姩瀛樺偍鍙橀噺">
+          <el-switch v-model="formData.autoStoreVariables" inline-prompt active-text="鏄�" inactive-text="鍚�" />
+        </el-form-item>
+        <el-form-item v-if="showConfig.ruleVariablesInput" prop="skipExpression" label="杈撳叆鍙橀噺">
+          <el-input v-model="formData.ruleVariablesInput"> </el-input>
+        </el-form-item>
+        <el-form-item v-if="showConfig.exclude" prop="exclude" label="鎺掗櫎">
+          <el-switch v-model="formData.exclude" inline-prompt active-text="鏄�" inactive-text="鍚�" />
+        </el-form-item>
+        <el-form-item v-if="showConfig.class" prop="class" label="绫�">
+          <el-input v-model="formData.class"> </el-input>
+        </el-form-item>
+      </el-collapse>
+    </el-form>
+    <UserSelect ref="userSelectRef" :data="formData.candidateUsers" @confirm-call-back="userSelectCallBack"></UserSelect>
+    <UserSelect ref="singleUserSelectRef" :data="formData.assignee" :multiple="false" @confirm-call-back="singleUserSelectCallBack"></UserSelect>
+    <RoleSelect ref="roleSelectRef" :data="formData.candidateGroups" @confirm-call-back="roleSelectCallBack"></RoleSelect>
+    <DueDate ref="dueDateRef" v-model="formData.dueDate" :data="formData.dueDate" @confirm-call-back="dueDateCallBack"></DueDate>
+  </div>
+</template>
+<script setup lang="ts">
+import useParseElement from '../hooks/useParseElement';
+import usePanel from '../hooks/usePanel';
+import UserSelect from '@/components/UserSelect';
+import RoleSelect from '@/components/RoleSelect';
+import ExecutionListener from './property/ExecutionListener.vue';
+import TaskListener from './property/TaskListener.vue';
+import DueDate from './property/DueDate.vue';
+import { ModdleElement } from 'bpmn';
+import { TaskPanel } from 'bpmnDesign';
+import { AllocationTypeEnum, MultiInstanceTypeEnum, SpecifyDescEnum } from '@/enums/bpmn/IndexEnums';
+import { UserVO } from '@/api/system/user/types';
+import { RoleVO } from '@/api/system/role/types';
+import { selectListFormManage } from '@/api/workflow/formManage';
+import { FormManageVO } from '@/api/workflow/formManage/types';
+const formManageList = ref<FormManageVO[]>([]);
+const formManageListLoading = ref(false);
+interface PropType {
+  element: ModdleElement;
+}
+const props = withDefaults(defineProps<PropType>(), {});
+const { showConfig, nameChange, formKeyChange, idChange, updateProperties, getExtensionElements, createModdleElement, constant } = usePanel({
+  element: toRaw(props.element)
+});
+const { parseData } = useParseElement({
+  element: toRaw(props.element)
+});
+
+const initFormData = {
+  id: '',
+  name: '',
+  dueDate: '',
+  multiInstanceType: MultiInstanceTypeEnum.NONE,
+  allocationType: AllocationTypeEnum.USER,
+  specifyDesc: SpecifyDescEnum.SPECIFY_SINGLE
+};
+const formData = ref({ ...initFormData, ...parseData<TaskPanel>() });
+const assignee = ref<Partial<UserVO>>({
+  userName: ''
+});
+const currentCollapseItem = ref(['1', '2']);
+const userSelectRef = ref<InstanceType<typeof UserSelect>>();
+const singleUserSelectRef = ref<InstanceType<typeof UserSelect>>();
+const roleSelectRef = ref<InstanceType<typeof RoleSelect>>();
+const dueDateRef = ref<InstanceType<typeof DueDate>>();
+
+const isMultiple = ref(true);
+const openUserSelect = () => {
+  userSelectRef.value.open();
+};
+const openSingleUserSelect = () => {
+  if (formData.value.assignee.includes('$')) {
+    formData.value.assignee = '';
+  }
+  singleUserSelectRef.value.open();
+};
+const openRoleSelect = () => {
+  roleSelectRef.value.open();
+};
+const openDueDate = (e) => {
+  dueDateRef.value.openDialog();
+};
+const blurAssignee = (assignee) => {
+  updateProperties({ 'flowable:assignee': assignee ? assignee : undefined });
+};
+const singleUserSelectCallBack = (data: UserVO[]) => {
+  const user: UserVO = data.length !== 0 ? data[0] : undefined;
+  updateProperties({ 'flowable:assignee': user?.userId });
+  assignee.value = user ? user : { userName: '' };
+  formData.value.assignee = String(user?.userId);
+  let extensionElements = getExtensionElements();
+  extensionElements.values = extensionElements.get('values').filter((item) => item.$type !== 'flowable:extAssignee');
+  if (user) {
+    const extAssigneeElement = createModdleElement('flowable:extAssignee', { body: '' }, extensionElements);
+    extensionElements.get('values').push(extAssigneeElement);
+    extAssigneeElement.body = JSON.stringify({ userName: user.userName, userId: user.userId });
+  }
+  if (extensionElements.values.length === 0) {
+    extensionElements = undefined;
+  }
+  updateProperties({ extensionElements: extensionElements });
+};
+const userSelectCallBack = (data: UserVO[]) => {
+  let extensionElements = getExtensionElements();
+  extensionElements.values = extensionElements.values.filter((item) => item.$type !== 'flowable:extCandidateUsers');
+  if (data.length === 0) {
+    formData.value.candidateUsers = undefined;
+    updateProperties({ 'flowable:candidateUsers': undefined });
+  } else {
+    const userIds = data.map((item) => item.userId).join(',');
+    formData.value.candidateUsers = userIds;
+    updateProperties({ 'flowable:candidateUsers': userIds });
+    const extCandidateUsersElement = createModdleElement('flowable:extCandidateUsers', { body: '' }, extensionElements);
+    extensionElements.values.push(extCandidateUsersElement);
+    const users = data.map((item) => {
+      return {
+        userId: item.userId,
+        userName: item.userName
+      };
+    });
+    extCandidateUsersElement.body = JSON.stringify(users);
+  }
+  if (extensionElements.values.length === 0) {
+    extensionElements = undefined;
+  }
+  updateProperties({ extensionElements: extensionElements });
+};
+const roleSelectCallBack = (data: RoleVO[]) => {
+  if (data.length === 0) {
+    formData.value.candidateGroups = '';
+    updateProperties({ 'flowable:candidateGroups': undefined });
+  } else {
+    const roleIds = data.map((item) => item.roleId).join(',');
+    formData.value.candidateGroups = roleIds;
+    updateProperties({ 'flowable:candidateGroups': roleIds });
+  }
+};
+const dueDateCallBack = (data: string) => {
+  updateProperties({ 'flowable:dueDate': data });
+};
+
+const taskTabClick = (e) => {
+  formData.value.candidateGroups = '';
+  formData.value.candidateUsers = '';
+  formData.value.assignee = '';
+  // formData.value.fixedAssignee = '';
+  assignee.value = {};
+};
+
+const syncChange = (newVal) => {
+  updateProperties({ 'flowable:async': newVal });
+};
+const skipExpressionChange = (newVal) => {
+  updateProperties({ 'flowable:skipExpression': newVal && newVal.length > 0 ? newVal : undefined });
+};
+const priorityChange = (newVal) => {
+  updateProperties({ 'flowable:priority': newVal });
+};
+const fixedAssigneeChange = (newVal) => {
+  updateProperties({ 'flowable:assignee': newVal && newVal.length > 0 ? newVal : undefined });
+};
+const multiInstanceTypeChange = (newVal) => {
+  if (newVal !== MultiInstanceTypeEnum.NONE) {
+    let loopCharacteristics = props.element.businessObject.get('loopCharacteristics');
+    if (!loopCharacteristics) {
+      loopCharacteristics = createModdleElement('bpmn:MultiInstanceLoopCharacteristics', {}, props.element.businessObject);
+    }
+    loopCharacteristics.isSequential = newVal === MultiInstanceTypeEnum.SERIAL;
+    updateProperties({ loopCharacteristics: loopCharacteristics });
+  } else {
+    updateProperties({ loopCharacteristics: undefined });
+  }
+};
+const collectionChange = (newVal) => {
+  let loopCharacteristics = props.element.businessObject.get('loopCharacteristics');
+  if (!loopCharacteristics) {
+    loopCharacteristics = createModdleElement('bpmn:MultiInstanceLoopCharacteristics', {}, props.element.businessObject);
+  }
+  loopCharacteristics.collection = newVal && newVal.length > 0 ? newVal : undefined;
+  updateProperties({ loopCharacteristics: loopCharacteristics });
+};
+const elementVariableChange = (newVal) => {
+  let loopCharacteristics = props.element.businessObject.get('loopCharacteristics');
+  if (!loopCharacteristics) {
+    loopCharacteristics = createModdleElement('bpmn:MultiInstanceLoopCharacteristics', {}, props.element.businessObject);
+  }
+  loopCharacteristics.elementVariable = newVal && newVal.length > 0 ? newVal : undefined;
+  updateProperties({ loopCharacteristics: loopCharacteristics });
+};
+const completionConditionChange = (newVal) => {
+  let loopCharacteristics = props.element.businessObject.get<ModdleElement>('loopCharacteristics');
+  if (!loopCharacteristics) {
+    loopCharacteristics = createModdleElement('bpmn:MultiInstanceLoopCharacteristics', {}, props.element.businessObject);
+  }
+  if (newVal && newVal.length > 0) {
+    if (!loopCharacteristics.completionCondition) {
+      loopCharacteristics.completionCondition = createModdleElement('bpmn:Expression', { body: newVal }, loopCharacteristics);
+    } else {
+      loopCharacteristics.completionCondition.body = newVal;
+    }
+  } else {
+    loopCharacteristics.completionCondition = undefined;
+  }
+  updateProperties({ loopCharacteristics: loopCharacteristics });
+};
+const dueDateChange = (newVal) => {
+  updateProperties({ 'flowable:dueDate': newVal && newVal.length > 0 ? newVal : undefined });
+};
+const selectUserLength = computed(() => {
+  if (formData.value.candidateUsers) {
+    return formData.value.candidateUsers.split(',').length;
+  } else {
+    return 0;
+  }
+});
+const selectRoleLength = computed(() => {
+  if (formData.value.candidateGroups) {
+    return formData.value.candidateGroups.split(',').length;
+  } else {
+    return 0;
+  }
+});
+
+onBeforeMount(() => {
+  const extensionElements = getExtensionElements(false);
+  if (extensionElements && extensionElements.get('values')) {
+    let extAssigneeElement = extensionElements.get('values').find((item) => item.$type === 'flowable:extAssignee');
+    if (extAssigneeElement) {
+      assignee.value = JSON.parse(extAssigneeElement.body);
+    }
+  }
+
+  if (formData.value.loopCharacteristics) {
+    const loopCharacteristics = formData.value.loopCharacteristics;
+    formData.value.collection = loopCharacteristics.collection || '';
+    formData.value.elementVariable = loopCharacteristics.elementVariable || '';
+    formData.value.completionCondition = loopCharacteristics.completionCondition?.body || '';
+    formData.value.multiInstanceType = loopCharacteristics.isSequential ? MultiInstanceTypeEnum.SERIAL : MultiInstanceTypeEnum.PARALLEL;
+  }
+
+  if (formData.value.assignee) {
+    formData.value.fixedAssignee = formData.value.assignee;
+  }
+});
+
+const formRules = ref<ElFormRules>({
+  id: [{ required: true, message: '璇疯緭鍏�', trigger: 'blur' }],
+  name: [{ required: true, message: '璇疯緭鍏�', trigger: 'blur' }]
+});
+
+const AllocationTypeSelect = [
+  { id: 'b9cdf970-dd91-47c0-819f-42a7010ca2a6', label: '鎸囧畾浜哄憳', value: AllocationTypeEnum.USER },
+  { id: '3f7ccbcd-c464-4602-bb9d-e96649d10585', label: '鍊欓�変汉鍛�', value: AllocationTypeEnum.CANDIDATE },
+  { id: 'c49065e0-7f2d-4c09-aedb-ab2d47d9a454', label: '鍙戣捣浜鸿嚜宸�', value: AllocationTypeEnum.YOURSELF },
+  { id: '6ef40a03-7e9a-4898-89b2-c88fe9064542', label: '鍙戣捣浜烘寚瀹�', value: AllocationTypeEnum.SPECIFY }
+];
+const SpecifyDesc = [
+  { id: 'fa253b34-4335-458c-b1bc-b039e2a2b7a6', label: '鎸囧畾涓�涓汉', value: 'specifySingle' },
+  { id: '7365ff54-2e05-4312-9bfb-0b8edd779c5b', label: '鎸囧畾澶氫釜浜�', value: 'specifyMultiple' }
+];
+
+const listFormManage = async () => {
+  formManageListLoading.value = true;
+  const res = await selectListFormManage();
+  formManageList.value = res.data;
+  formManageListLoading.value = false;
+};
+onMounted(() => {
+  nextTick(() => {
+    listFormManage();
+  });
+});
+</script>
+
+<style lang="scss" scoped></style>
diff --git a/src/bpmn/panel/index.vue b/src/bpmn/panel/index.vue
new file mode 100644
index 0000000..eecb0cb
--- /dev/null
+++ b/src/bpmn/panel/index.vue
@@ -0,0 +1,110 @@
+<template>
+  <div ref="propertyPanel">
+    <div v-if="nodeName" class="node-name">{{ nodeName }}</div>
+    <component :is="component" v-if="element" :element="element" />
+  </div>
+</template>
+<script setup lang="ts" name="PropertyPanel">
+import { NodeName } from '../assets/lang/zh';
+import TaskPanel from './TaskPanel.vue';
+import ProcessPanel from './ProcessPanel.vue';
+import StartEndPanel from './StartEndPanel.vue';
+import GatewayPanel from './GatewayPanel.vue';
+import SequenceFlowPanel from './SequenceFlowPanel.vue';
+import ParticipantPanel from './ParticipantPanel.vue';
+import SubProcessPanel from './SubProcessPanel.vue';
+import { Modeler, ModdleElement } from 'bpmn';
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+interface propsType {
+  modeler: Modeler;
+}
+const props = withDefaults(defineProps<propsType>(), {});
+
+const element = ref<ModdleElement>();
+const processElement = ref<ModdleElement>();
+
+const startEndType = ['bpmn:IntermediateThrowEvent', 'bpmn:StartEvent', 'bpmn:EndEvent'];
+const taskType = [
+  'bpmn:UserTask',
+  'bpmn:Task',
+  'bpmn:SendTask',
+  'bpmn:ReceiveTask',
+  'bpmn:ManualTask',
+  'bpmn:BusinessRuleTask',
+  'bpmn:ServiceTask',
+  'bpmn:ScriptTask'
+];
+const sequenceType = ['bpmn:SequenceFlow'];
+const gatewayType = ['bpmn:InclusiveGateway', 'bpmn:ExclusiveGateway', 'bpmn:ParallelGateway', 'bpmn:EventBasedGateway', 'bpmn:ComplexGateway'];
+const processType = ['bpmn:Process'];
+
+// 缁勪欢璁$畻
+const component = computed(() => {
+  if (!element.value) return null;
+  const type = element.value.type;
+  if (startEndType.includes(type)) return StartEndPanel;
+  if (taskType.includes(type)) return TaskPanel;
+  if (sequenceType.includes(type)) return SequenceFlowPanel;
+  if (gatewayType.includes(type)) return GatewayPanel;
+  if (processType.includes(type)) return ProcessPanel;
+  if (type === 'bpmn:Participant') return ParticipantPanel;
+  if (type === 'bpmn:SubProcess') return SubProcessPanel;
+  //return proxy?.$modal.msgWarning('闈㈡澘寮�鍙戜腑....');
+  return undefined;
+});
+
+const nodeName = computed(() => {
+  if (element.value) {
+    const bizObj = element.value.businessObject;
+    const type = bizObj?.eventDefinitions && bizObj?.eventDefinitions.length > 0 ? bizObj.eventDefinitions[0].$type : bizObj.$type;
+    return NodeName[type] || type;
+  }
+  return '';
+});
+
+const handleModeler = () => {
+  props.modeler.on('root.added', (e: any) => {
+    element.value = null;
+    if (e.element.type === 'bpmn:Process') {
+      nextTick(() => {
+        element.value = e.element;
+        processElement.value = e.element;
+      });
+    }
+  });
+  props.modeler.on('element.click', (e: any) => {
+    if (e.element.type === 'bpmn:Process') {
+      nextTick(() => {
+        element.value = e.element;
+        processElement.value = e.element;
+      });
+    }
+  });
+  props.modeler.on('selection.changed', (e: any) => {
+    // 鍏堢粰null涓轰簡璁﹙ue鍒锋柊
+    element.value = null;
+    const newElement = e.newSelection[0];
+    if (newElement) {
+      nextTick(() => {
+        element.value = newElement;
+      });
+    } else {
+      nextTick(() => {
+        element.value = processElement.value;
+      });
+    }
+  });
+};
+
+onMounted(() => {
+  handleModeler();
+});
+</script>
+
+<style scoped lang="scss">
+.node-name {
+  font-size: 16px;
+  font-weight: bold;
+  padding: 10px;
+}
+</style>
diff --git a/src/bpmn/panel/property/DueDate.vue b/src/bpmn/panel/property/DueDate.vue
new file mode 100644
index 0000000..882766b
--- /dev/null
+++ b/src/bpmn/panel/property/DueDate.vue
@@ -0,0 +1,252 @@
+<template>
+  <div>
+    <el-dialog v-model="visible" :title="title" width="600px" append-to-body>
+      <el-form label-width="100px">
+        <el-form-item label="灏忔椂">
+          <el-radio-group v-model="hourValue" @change="hourChange">
+            <el-radio-button label="4" value="4" />
+            <el-radio-button label="8" value="8" />
+            <el-radio-button label="12" value="12" />
+            <el-radio-button label="24" value="24" />
+            <el-radio-button label="鑷畾涔�" value="鑷畾涔�" />
+            <el-input-number v-show="hourValue === '鑷畾涔�'" v-model="customHourValue" :min="1" @change="customHourValueChange"></el-input-number>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="澶�">
+          <el-radio-group v-model="dayValue" @change="dayChange">
+            <el-radio-button label="1" value="1" />
+            <el-radio-button label="2" value="2" />
+            <el-radio-button label="3" value="3" />
+            <el-radio-button label="4" value="4" />
+            <el-radio-button label="鑷畾涔�" value="鑷畾涔�" />
+            <el-input-number v-show="dayValue === '鑷畾涔�'" v-model="customDayValue" :min="1" @change="customDayValueChange"></el-input-number>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="鍛�">
+          <el-radio-group v-model="weekValue" @change="weekChange">
+            <el-radio-button label="1" value="1" />
+            <el-radio-button label="2" value="2" />
+            <el-radio-button label="3" value="3" />
+            <el-radio-button label="4" value="4" />
+            <el-radio-button label="鑷畾涔�" value="鑷畾涔�" />
+            <el-input-number v-show="weekValue === '鑷畾涔�'" v-model="customWeekValue" :min="1" @change="customWeekValueChange"></el-input-number>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="鏈�">
+          <el-radio-group v-model="monthValue" @change="monthChange">
+            <el-radio-button label="1" value="1" />
+            <el-radio-button label="2" value="2" />
+            <el-radio-button label="3" value="3" />
+            <el-radio-button label="4" value="4" />
+            <el-radio-button label="鑷畾涔�" value="鑷畾涔�" />
+            <el-input-number v-show="monthValue === '鑷畾涔�'" v-model="customMonthValue" :min="1" @change="customMonthValueChange"></el-input-number>
+          </el-radio-group>
+        </el-form-item>
+      </el-form>
+
+      <template #footer>
+        <div>
+          <el-button @click="closeDialog">鍙栨秷</el-button>
+          <el-button type="primary" @click="confirm">纭畾</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup lang="ts">
+import useDialog from '@/hooks/useDialog';
+
+interface PropType {
+  modelValue?: string;
+  data?: string;
+}
+const prop = withDefaults(defineProps<PropType>(), {
+  modelValue: '',
+  data: ''
+});
+const emit = defineEmits(['update:modelValue', 'confirmCallBack']);
+
+const { title, visible, openDialog, closeDialog } = useDialog({
+  title: '璁剧疆浠诲姟鍒版湡鏃堕棿'
+});
+const formValue = ref();
+const valueType = ref();
+
+const hourValue = ref('');
+const dayValue = ref('');
+const weekValue = ref('');
+const monthValue = ref('');
+
+const customHourValue = ref(1);
+const customDayValue = ref(1);
+const customWeekValue = ref(1);
+const customMonthValue = ref(1);
+
+const hourValueConst = ['4', '8', '12', '24'];
+const dayAndWeekAndMonthValueConst = ['1', '2', '3', '4'];
+
+const initValue = () => {
+  formValue.value = prop.data;
+  if (prop.data) {
+    const lastStr = prop.data.substring(prop.data.length - 1);
+    if (lastStr === 'H') {
+      const hourValueValue = prop.data.substring(2, prop.data.length - 1);
+      if (hourValueConst.includes(hourValueValue)) {
+        hourValue.value = hourValueValue;
+      } else {
+        hourValue.value = '鑷畾涔�';
+        customHourValue.value = Number(hourValueValue);
+      }
+    }
+    const dayAndWeekAndMonthValue = prop.data.substring(1, prop.data.length - 1);
+    if (lastStr === 'D') {
+      if (dayAndWeekAndMonthValueConst.includes(dayAndWeekAndMonthValue)) {
+        dayValue.value = dayAndWeekAndMonthValue;
+      } else {
+        dayValue.value = '鑷畾涔�';
+        customDayValue.value = Number(dayAndWeekAndMonthValue);
+      }
+    }
+    if (lastStr === 'W') {
+      if (dayAndWeekAndMonthValueConst.includes(dayAndWeekAndMonthValue)) {
+        weekValue.value = dayAndWeekAndMonthValue;
+      } else {
+        weekValue.value = '鑷畾涔�';
+        customWeekValue.value = Number(dayAndWeekAndMonthValue);
+      }
+    }
+    if (lastStr === 'M') {
+      if (dayAndWeekAndMonthValueConst.includes(dayAndWeekAndMonthValue)) {
+        monthValue.value = dayAndWeekAndMonthValue;
+      } else {
+        monthValue.value = '鑷畾涔�';
+        customMonthValue.value = Number(dayAndWeekAndMonthValue);
+      }
+    }
+  }
+};
+
+const confirm = () => {
+  emit('update:modelValue', formValue.value);
+  emit('confirmCallBack', formValue.value);
+  closeDialog();
+};
+
+const customHourValueChange = (customHourValue) => {
+  formValue.value = `PT${customHourValue}H`;
+
+  dayValue.value = '';
+  weekValue.value = '';
+  monthValue.value = '';
+  customDayValue.value = 1;
+  customWeekValue.value = 1;
+  customMonthValue.value = 1;
+};
+const customDayValueChange = (customDayValue) => {
+  formValue.value = `P${customDayValue}D`;
+  hourValue.value = '';
+  weekValue.value = '';
+  monthValue.value = '';
+
+  customHourValue.value = 1;
+  customWeekValue.value = 1;
+  customMonthValue.value = 1;
+};
+
+const customWeekValueChange = (customWeekValue) => {
+  formValue.value = `P${customWeekValue}W`;
+  hourValue.value = '';
+  dayValue.value = '';
+  monthValue.value = '';
+
+  customHourValue.value = 1;
+  customDayValue.value = 1;
+  customMonthValue.value = 1;
+};
+
+const customMonthValueChange = (customMonthValue) => {
+  formValue.value = `P${customMonthValue}M`;
+  hourValue.value = '';
+  dayValue.value = '';
+  weekValue.value = '';
+
+  customHourValue.value = 1;
+  customDayValue.value = 1;
+  customWeekValue.value = 1;
+};
+
+const hourChange = (hourValue) => {
+  if (hourValue === '鑷畾涔�') {
+    formValue.value = `PT${customHourValue.value}H`;
+  } else {
+    formValue.value = `PT${hourValue}H`;
+  }
+
+  dayValue.value = '';
+  weekValue.value = '';
+  monthValue.value = '';
+  customDayValue.value = 1;
+  customWeekValue.value = 1;
+  customMonthValue.value = 1;
+};
+const dayChange = (dayValue) => {
+  if (dayValue === '鑷畾涔�') {
+    formValue.value = `P${customDayValue.value}D`;
+  } else {
+    formValue.value = `P${dayValue}D`;
+  }
+
+  hourValue.value = '';
+  weekValue.value = '';
+  monthValue.value = '';
+
+  customHourValue.value = 1;
+  customWeekValue.value = 1;
+  customMonthValue.value = 1;
+};
+const weekChange = (weekValue) => {
+  if (weekValue === '鑷畾涔�') {
+    formValue.value = `P${customWeekValue.value}W`;
+  } else {
+    formValue.value = `P${weekValue}W`;
+  }
+
+  hourValue.value = '';
+  dayValue.value = '';
+  monthValue.value = '';
+
+  customHourValue.value = 1;
+  customDayValue.value = 1;
+  customMonthValue.value = 1;
+};
+const monthChange = (monthValue) => {
+  if (monthValue === '鑷畾涔�') {
+    formValue.value = `P${customMonthValue.value}M`;
+  } else {
+    formValue.value = `P${monthValue}M`;
+  }
+
+  hourValue.value = '';
+  dayValue.value = '';
+  weekValue.value = '';
+
+  customHourValue.value = 1;
+  customDayValue.value = 1;
+  customWeekValue.value = 1;
+};
+
+watch(
+  () => visible.value,
+  () => {
+    if (visible.value) {
+      initValue();
+    }
+  }
+);
+
+defineExpose({
+  openDialog,
+  closeDialog
+});
+</script>
diff --git a/src/bpmn/panel/property/ExecutionListener.vue b/src/bpmn/panel/property/ExecutionListener.vue
new file mode 100644
index 0000000..7620cf3
--- /dev/null
+++ b/src/bpmn/panel/property/ExecutionListener.vue
@@ -0,0 +1,308 @@
+<template>
+  <div>
+    <vxe-toolbar>
+      <template #buttons>
+        <el-button type="primary" link size="small" @click="insertEvent">鏂板</el-button>
+        <el-button type="primary" link size="small" @click="removeSelectRowEvent">鍒犻櫎</el-button>
+      </template>
+    </vxe-toolbar>
+    <vxe-table
+      ref="tableRef"
+      size="mini"
+      height="100px"
+      border
+      show-overflow
+      keep-source
+      :data="tableData"
+      :menu-config="menuConfig"
+      @cell-dblclick="cellDBLClickEvent"
+      @menu-click="contextMenuClickEvent"
+    >
+      <vxe-column type="checkbox" width="40"></vxe-column>
+      <vxe-column type="seq" width="40"></vxe-column>
+      <vxe-column field="event" title="浜嬩欢" min-width="100px">
+        <template #default="slotParams">
+          <span>{{ eventSelect.find((e) => e.value === slotParams.row.event)?.label }}</span>
+        </template>
+      </vxe-column>
+      <vxe-column field="type" title="绫诲瀷" min-width="100px">
+        <template #default="slotParams">
+          <span>{{ typeSelect.find((e) => e.value === slotParams.row.type)?.label }}</span>
+        </template>
+      </vxe-column>
+      <vxe-column field="className" title="Java 绫诲悕" min-width="100px"> </vxe-column>
+    </vxe-table>
+
+    <el-dialog
+      v-model="formDialog.visible.value"
+      :title="formDialog.title.value"
+      width="600px"
+      :close-on-click-modal="false"
+      :close-on-press-escape="false"
+      :show-close="false"
+      append-to-body
+    >
+      <el-form ref="formRef" :model="formData" :rules="tableRules" label-width="100px">
+        <el-form-item label="浜嬩欢" prop="event">
+          <el-select v-model="formData.event">
+            <el-option v-for="item in eventSelect" :key="item.id" :value="item.value" :label="item.label"></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="绫诲瀷" prop="type">
+          <template #label>
+            <span>
+              绫诲瀷
+              <el-tooltip placement="top">
+                <el-icon><QuestionFilled /></el-icon>
+                <template #content>
+                  绫伙細绀轰緥 com.company.MyCustomListener锛岃嚜瀹氫箟绫诲繀椤诲疄鐜� org.flowable.engine.delegate.TaskListener 鎺ュ彛<br />
+                  琛ㄨ揪寮忥細绀轰緥 ${myObject.callMethod(task, task.eventName)}<br />
+                  濮旀墭琛ㄨ揪寮忥細绀轰緥 ${myListenerSpringBean} 锛岃 springBean 闇�瑕佸疄鐜� org.flowable.engine.delegate.TaskListener 鎺ュ彛
+                </template>
+              </el-tooltip>
+            </span>
+          </template>
+          <el-select v-model="formData.type">
+            <el-option v-for="item in typeSelect" :key="item.id" :value="item.value" :label="item.label"></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item
+          :label="typeSelect.filter((e) => e.value === formData.type)[0] ? typeSelect.filter((e) => e.value === formData.type)[0]?.label : '琛ㄨ揪寮�'"
+          prop="className"
+        >
+          <el-input v-model="formData.className" type="text"></el-input>
+        </el-form-item>
+      </el-form>
+      <el-tabs type="border-card">
+        <el-tab-pane label="鍙傛暟">
+          <ListenerParam ref="listenerParamRef" :table-data="formData.params" />
+        </el-tab-pane>
+      </el-tabs>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="formDialog.closeDialog">鍙� 娑�</el-button>
+          <el-button type="primary" @click="submitEvent">纭� 瀹�</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+<script setup lang="ts">
+import ListenerParam from './ListenerParam.vue';
+import { VxeTableEvents, VxeTableInstance, VxeTablePropTypes } from 'vxe-table';
+import { ExecutionListenerVO } from 'bpmnDesign';
+import { Moddle, Modeler, ModdleElement } from 'bpmn';
+
+import usePanel from '../../hooks/usePanel';
+import useDialog from '@/hooks/useDialog';
+import useModelerStore from '@/store/modules/modeler';
+
+const emit = defineEmits(['close']);
+interface PropType {
+  element: ModdleElement;
+}
+const props = withDefaults(defineProps<PropType>(), {});
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const selectRow = ref<ExecutionListenerVO | null>();
+const formDialog = useDialog({
+  title: selectRow.value ? '缂栬緫&淇濆瓨' : '鏂板&淇濆瓨'
+});
+
+const { showConfig, elementType, updateProperties } = usePanel({
+  element: toRaw(props.element)
+});
+const { getModdle } = useModelerStore();
+const moddle = getModdle();
+
+const listenerParamRef = ref<InstanceType<typeof ListenerParam>>();
+const tableRef = ref<VxeTableInstance<ExecutionListenerVO>>();
+const formRef = ref<ElFormInstance>();
+
+const initData: ExecutionListenerVO = {
+  event: '',
+  type: '',
+  className: '',
+  params: []
+};
+const formData = ref<ExecutionListenerVO>({ ...initData });
+const tableData = ref<ExecutionListenerVO[]>([]);
+const tableRules = ref<ElFormRules>({
+  event: [{ required: true, message: '璇烽�夋嫨', trigger: 'blur' }],
+  type: [{ required: true, message: '璇烽�夋嫨', trigger: 'blur' }],
+  className: [{ required: true, message: '璇疯緭鍏�', trigger: 'blur' }]
+});
+
+const submitEvent = async () => {
+  const error = await listenerParamRef.value.validate();
+  await formRef.value.validate((validate) => {
+    if (validate && !error) {
+      const $table = tableRef.value;
+      if ($table) {
+        formData.value.params = listenerParamRef.value.getTableData();
+        if (selectRow.value) {
+          Object.assign(selectRow.value, formData.value);
+        } else {
+          $table.insertAt({ ...formData.value }, -1);
+        }
+        updateElement();
+        formDialog.closeDialog();
+      }
+    }
+  });
+};
+
+const removeSelectRowEvent = async () => {
+  const $table = tableRef.value;
+  if ($table) {
+    const selectCount = $table.getCheckboxRecords().length;
+    if (selectCount === 0) {
+      proxy?.$modal.msgWarning('璇烽�夋嫨琛�');
+    } else {
+      await $table.removeCheckboxRow();
+      updateElement();
+    }
+  }
+};
+const insertEvent = async () => {
+  Object.assign(formData.value, initData);
+  selectRow.value = null;
+  formDialog.openDialog();
+};
+
+const editEvent = (row: ExecutionListenerVO) => {
+  Object.assign(formData.value, row);
+  selectRow.value = row;
+  formDialog.openDialog();
+};
+
+const removeEvent = async (row: ExecutionListenerVO) => {
+  await proxy?.$modal.confirm('鎮ㄧ‘瀹氳鍒犻櫎璇ユ暟鎹�?');
+  const $table = tableRef.value;
+  if ($table) {
+    await $table.remove(row);
+    updateElement();
+  }
+};
+const updateElement = () => {
+  const $table = tableRef.value;
+  const data = $table.getTableData().fullData;
+  if (data.length) {
+    let extensionElements = props.element.businessObject.get('extensionElements');
+    if (!extensionElements) {
+      extensionElements = moddle.create('bpmn:ExtensionElements');
+    }
+    // 娓呴櫎鏃у��
+    extensionElements.values = extensionElements.values?.filter((item) => item.$type !== 'flowable:ExecutionListener') ?? [];
+    data.forEach((item) => {
+      const executionListener = moddle.create('flowable:ExecutionListener');
+      executionListener['event'] = item.event;
+      executionListener[item.type] = item.className;
+      if (item.params && item.params.length) {
+        item.params.forEach((field) => {
+          const fieldElement = moddle.create('flowable:Field');
+          fieldElement['name'] = field.name;
+          fieldElement[field.type] = field.value;
+          executionListener.get('fields').push(fieldElement);
+        });
+      }
+      extensionElements.get('values').push(executionListener);
+    });
+    updateProperties({ extensionElements: extensionElements });
+  } else {
+    const extensionElements = props.element.businessObject[`extensionElements`];
+    if (extensionElements) {
+      extensionElements.values = extensionElements.values?.filter((item) => item.$type !== 'flowable:ExecutionListener') ?? [];
+    }
+  }
+};
+
+const cellDBLClickEvent: VxeTableEvents.CellDblclick<ExecutionListenerVO> = ({ row }) => {
+  editEvent(row);
+};
+
+const menuConfig = reactive<VxeTablePropTypes.MenuConfig<ExecutionListenerVO>>({
+  body: {
+    options: [
+      [
+        { code: 'edit', name: '缂栬緫', prefixIcon: 'vxe-icon-edit', disabled: false },
+        { code: 'remove', name: '鍒犻櫎', prefixIcon: 'vxe-icon-delete', disabled: false }
+      ]
+    ]
+  },
+  visibleMethod({ options, column }) {
+    const isDisabled = !column;
+    options.forEach((list) => {
+      list.forEach((item) => {
+        item.disabled = isDisabled;
+      });
+    });
+    return true;
+  }
+});
+const contextMenuClickEvent: VxeTableEvents.MenuClick<ExecutionListenerVO> = ({ menu, row, column }) => {
+  const $table = tableRef.value;
+  if ($table) {
+    switch (menu.code) {
+      case 'edit':
+        editEvent(row);
+        break;
+      case 'remove':
+        removeEvent(row);
+        break;
+    }
+  }
+};
+
+const initTableData = () => {
+  tableData.value =
+    props.element.businessObject.extensionElements?.values
+      .filter((item) => item.$type === 'flowable:ExecutionListener')
+      .map((item) => {
+        let type;
+        if ('class' in item) type = 'class';
+        if ('expression' in item) type = 'expression';
+        if ('delegateExpression' in item) type = 'delegateExpression';
+        return {
+          event: item.event,
+          type: type,
+          className: item[type],
+          params:
+            item.fields?.map((field) => {
+              let fieldType;
+              if ('stringValue' in field) fieldType = 'stringValue';
+              if ('expression' in field) fieldType = 'expression';
+              return {
+                name: field.name,
+                type: fieldType,
+                value: field[fieldType]
+              };
+            }) ?? []
+        };
+      }) ?? [];
+};
+
+onMounted(() => {
+  initTableData();
+});
+
+const typeSelect = [
+  { id: '742fdeb7-23b4-416b-ac66-cd4ec8b901b7', label: '绫�', value: 'class' },
+  { id: '660c9c46-8fae-4bae-91a0-0335420019dc', label: '琛ㄨ揪寮�', value: 'expression' },
+  { id: '4b8135ab-6bc3-4a0f-80be-22f58bc6c5fd', label: '濮旀墭琛ㄨ揪寮�', value: 'delegateExpression' }
+];
+const eventSelect = [
+  { id: 'e6e0a51a-2d5d-4dc4-b847-b5c14f43a6ab', label: '寮�濮�', value: 'start' },
+  { id: '6da97c1e-15fc-4445-8943-75d09f49778e', label: '缁撴潫', value: 'end' },
+  { id: '6a2cbcec-e026-4f11-bef7-fff0b5c871e2', label: '鍚敤', value: 'take' }
+];
+</script>
+
+<style scoped lang="scss">
+.el-badge {
+  :deep(.el-badge__content) {
+    top: 10px;
+  }
+}
+</style>
diff --git a/src/bpmn/panel/property/ListenerParam.vue b/src/bpmn/panel/property/ListenerParam.vue
new file mode 100644
index 0000000..334249b
--- /dev/null
+++ b/src/bpmn/panel/property/ListenerParam.vue
@@ -0,0 +1,121 @@
+<template>
+  <vxe-toolbar>
+    <template #buttons>
+      <el-button icon="Plus" @click="insertRow">鏂板</el-button>
+    </template>
+  </vxe-toolbar>
+  <vxe-table
+    ref="tableRef"
+    :height="height"
+    border
+    show-overflow
+    keep-source
+    :data="tableData"
+    :edit-rules="tableRules"
+    :edit-config="{ trigger: 'click', mode: 'row', showStatus: true }"
+  >
+    <vxe-column type="seq" width="40"></vxe-column>
+    <vxe-column field="type" title="绫诲瀷" :edit-render="{}">
+      <template #default="slotParams">
+        <span>{{ typeSelect.find((e) => e.value === slotParams.row.type)?.label }}</span>
+      </template>
+      <template #edit="slotParams">
+        <vxe-select v-model="slotParams.row.type">
+          <vxe-option v-for="item in typeSelect" :key="item.id" :value="item.value" :label="item.label"></vxe-option>
+        </vxe-select>
+      </template>
+    </vxe-column>
+    <vxe-column field="name" title="鍚嶇О" :edit-render="{}">
+      <template #edit="slotParams">
+        <vxe-input v-model="slotParams.row.name" type="text"></vxe-input>
+      </template>
+    </vxe-column>
+    <vxe-column field="value" title="鍊�" :edit-render="{}">
+      <template #edit="slotParams">
+        <vxe-input v-model="slotParams.row.value" type="text"></vxe-input>
+      </template>
+    </vxe-column>
+    <vxe-column title="鎿嶄綔" width="100" show-overflow align="center">
+      <template #default="slotParams">
+        <el-tooltip content="鍒犻櫎" placement="top">
+          <el-button link type="danger" icon="Delete" @click="removeRow(slotParams.row)"></el-button>
+        </el-tooltip>
+      </template>
+    </vxe-column>
+  </vxe-table>
+</template>
+
+<script setup lang="ts">
+import { VXETable, VxeTableInstance, VxeTablePropTypes } from 'vxe-table';
+import { ParamVO } from 'bpmnDesign';
+import useDialog from '@/hooks/useDialog';
+
+interface PropType {
+  height?: string;
+  tableData?: ParamVO[];
+}
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const props = withDefaults(defineProps<PropType>(), {
+  height: '200px',
+  tableData: () => []
+});
+
+const tableRules = ref<VxeTablePropTypes.EditRules>({
+  type: [{ required: true, message: '璇烽�夋嫨', trigger: 'blur' }],
+  name: [{ required: true, message: '璇疯緭鍏�', trigger: 'blur' }],
+  value: [{ required: true, message: '璇疯緭鍏�', trigger: 'blur' }]
+});
+
+const { title, visible, openDialog, closeDialog } = useDialog({
+  title: '鐩戝惉鍣ㄥ弬鏁�'
+});
+const typeSelect = [
+  { id: '742fdeb7-23b4-416b-ac66-cd4ec8b901b7', label: '瀛楃涓�', value: 'stringValue' },
+  { id: '660c9c46-8fae-4bae-91a0-0335420019dc', label: '琛ㄨ揪寮�', value: 'expression' }
+];
+
+const tableRef = ref<VxeTableInstance<ParamVO>>();
+
+const getTableData = () => {
+  const $table = tableRef.value;
+  if ($table) {
+    return $table.getTableData().fullData;
+  }
+  return [];
+};
+
+const insertRow = async () => {
+  const $table = tableRef.value;
+  if ($table) {
+    const { row: newRow } = await $table.insertAt({}, -1);
+    // 鎻掑叆涓�鏉℃暟鎹苟瑙﹀彂鏍¢獙
+    await $table.validate(newRow);
+  }
+};
+
+const removeRow = async (row: ParamVO) => {
+  await proxy?.$modal.confirm('鎮ㄧ‘瀹氳鍒犻櫎璇ユ暟鎹�?');
+  const $table = tableRef.value;
+  if ($table) {
+    await $table.remove(row);
+  }
+};
+
+const validate = async () => {
+  const $table = tableRef.value;
+  if ($table) {
+    return await $table.validate(true);
+  }
+};
+
+defineExpose({
+  closeDialog,
+  openDialog,
+  validate,
+  getTableData
+});
+</script>
+
+<style scoped lang="scss"></style>
diff --git a/src/bpmn/panel/property/TaskListener.vue b/src/bpmn/panel/property/TaskListener.vue
new file mode 100644
index 0000000..db774e4
--- /dev/null
+++ b/src/bpmn/panel/property/TaskListener.vue
@@ -0,0 +1,310 @@
+<template>
+  <div>
+    <vxe-toolbar>
+      <template #buttons>
+        <el-button type="primary" link size="small" @click="insertEvent">鏂板</el-button>
+        <el-button type="primary" link size="small" @click="removeSelectRowEvent">鍒犻櫎</el-button>
+      </template>
+    </vxe-toolbar>
+    <vxe-table
+      ref="tableRef"
+      size="mini"
+      height="100px"
+      border
+      show-overflow
+      keep-source
+      :data="tableData"
+      :menu-config="menuConfig"
+      @cell-dblclick="cellDBLClickEvent"
+      @menu-click="contextMenuClickEvent"
+    >
+      <vxe-column type="checkbox" width="40"></vxe-column>
+      <vxe-column type="seq" width="40"></vxe-column>
+      <vxe-column field="event" title="浜嬩欢" min-width="100px">
+        <template #default="slotParams">
+          <span>{{ eventSelect.find((e) => e.value === slotParams.row.event)?.label }}</span>
+        </template>
+      </vxe-column>
+      <vxe-column field="type" title="绫诲瀷" min-width="100px">
+        <template #default="slotParams">
+          <span>{{ typeSelect.find((e) => e.value === slotParams.row.type)?.label }}</span>
+        </template>
+      </vxe-column>
+      <vxe-column field="className" title="Java 绫诲悕" min-width="100px"> </vxe-column>
+    </vxe-table>
+
+    <el-dialog
+      v-model="formDialog.visible.value"
+      :title="formDialog.title.value"
+      width="600px"
+      :close-on-click-modal="false"
+      :close-on-press-escape="false"
+      :show-close="false"
+      append-to-body
+    >
+      <el-form ref="formRef" :model="formData" :rules="tableRules" label-width="100px">
+        <el-form-item label="浜嬩欢" prop="event">
+          <template #label>
+            <span>
+              浜嬩欢
+              <el-tooltip placement="top">
+                <el-icon><QuestionFilled /></el-icon>
+                <template #content>
+                  create锛堝垱寤猴級锛氬綋浠诲姟宸茬粡鍒涘缓锛屽苟涓旀墍鏈変换鍔″弬鏁伴兘宸茬粡璁剧疆鏃惰Е鍙戙��<br />
+                  assignment锛堟寚娲撅級锛氬綋浠诲姟宸茬粡鎸囨淳缁欐煇浜烘椂瑙﹀彂銆傝娉ㄦ剰锛氬綋娴佺▼鎵ц鍒拌揪鐢ㄦ埛浠诲姟鏃讹紝鍦ㄨЕ鍙慶reate浜嬩欢涔嬪墠锛屼細棣栧厛瑙﹀彂assignment浜嬩欢銆�<br />
+                  complete锛堝畬鎴愶級锛氬綋浠诲姟宸茬粡瀹屾垚锛屼粠杩愯鏃舵暟鎹腑鍒犻櫎鍓嶈Е鍙戙��<br />
+                  delete锛堝垹闄わ級锛氬湪浠诲姟鍗冲皢琚垹闄ゅ墠瑙﹀彂銆傝娉ㄦ剰浠诲姟鐢眂ompleteTask姝e父瀹屾垚鏃朵篃浼氳Е鍙戙��
+                </template>
+              </el-tooltip>
+            </span>
+          </template>
+          <el-select v-model="formData.event">
+            <el-option v-for="item in eventSelect" :key="item.id" :value="item.value" :label="item.label"></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="绫诲瀷" prop="type">
+          <el-select v-model="formData.type">
+            <el-option v-for="item in typeSelect" :key="item.id" :value="item.value" :label="item.label"></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item
+          :label="typeSelect.filter((e) => e.value === formData.type)[0] ? typeSelect.filter((e) => e.value === formData.type)[0]?.label : '琛ㄨ揪寮�'"
+          prop="className"
+        >
+          <el-input v-model="formData.className" type="text"></el-input>
+        </el-form-item>
+      </el-form>
+      <el-tabs type="border-card">
+        <el-tab-pane label="鍙傛暟">
+          <ListenerParam ref="listenerParamRef" :table-data="formData.params" />
+        </el-tab-pane>
+      </el-tabs>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="formDialog.closeDialog">鍙� 娑�</el-button>
+          <el-button type="primary" @click="submitEvent">纭� 瀹�</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+<script setup lang="ts">
+import ListenerParam from './ListenerParam.vue';
+import { VxeTableEvents, VxeTableInstance, VxeTablePropTypes } from 'vxe-table';
+import { TaskListenerVO } from 'bpmnDesign';
+import { ModdleElement } from 'bpmn';
+
+import usePanel from '../../hooks/usePanel';
+import useDialog from '@/hooks/useDialog';
+import useModelerStore from '@/store/modules/modeler';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+interface PropType {
+  element: ModdleElement;
+}
+const props = withDefaults(defineProps<PropType>(), {});
+
+const selectRow = ref<TaskListenerVO | null>();
+const formDialog = useDialog({
+  title: selectRow.value ? '缂栬緫&淇濆瓨' : '鏂板&淇濆瓨'
+});
+const { showConfig, elementType, updateProperties } = usePanel({
+  element: toRaw(props.element)
+});
+const { getModdle } = useModelerStore();
+const moddle = getModdle();
+
+const listenerParamRef = ref<InstanceType<typeof ListenerParam>>();
+const tableRef = ref<VxeTableInstance<TaskListenerVO>>();
+const formRef = ref<ElFormInstance>();
+
+const initData: TaskListenerVO = {
+  event: '',
+  type: '',
+  className: '',
+  name: '',
+  params: []
+};
+const formData = ref<TaskListenerVO>({ ...initData });
+const currentIndex = ref(0);
+const tableData = ref<TaskListenerVO[]>([]);
+const tableRules = ref<VxeTablePropTypes.EditRules>({
+  event: [{ required: true, message: '璇烽�夋嫨', trigger: 'blur' }],
+  type: [{ required: true, message: '璇烽�夋嫨', trigger: 'blur' }],
+  name: [{ required: true, message: '璇疯緭鍏�', trigger: 'blur' }],
+  className: [{ required: true, message: '璇疯緭鍏�', trigger: 'blur' }]
+});
+
+const submitEvent = async () => {
+  const error = await listenerParamRef.value.validate();
+  await formRef.value.validate((validate) => {
+    if (validate && !error) {
+      const $table = tableRef.value;
+      if ($table) {
+        formData.value.params = listenerParamRef.value.getTableData();
+        if (selectRow.value) {
+          Object.assign(selectRow.value, formData.value);
+        } else {
+          $table.insertAt({ ...formData.value }, -1);
+        }
+        updateElement();
+        formDialog.closeDialog();
+      }
+    }
+  });
+};
+
+const insertEvent = async () => {
+  Object.assign(formData.value, initData);
+  selectRow.value = null;
+  formDialog.openDialog();
+};
+
+const editEvent = (row: TaskListenerVO) => {
+  Object.assign(formData.value, row);
+  selectRow.value = row;
+  formDialog.openDialog();
+};
+const removeEvent = async (row: TaskListenerVO) => {
+  await proxy?.$modal.confirm('鎮ㄧ‘瀹氳鍒犻櫎璇ユ暟鎹�?');
+  const $table = tableRef.value;
+  if ($table) {
+    await $table.remove(row);
+    updateElement();
+  }
+};
+
+const removeSelectRowEvent = async () => {
+  const $table = tableRef.value;
+  if ($table) {
+    const selectCount = $table.getCheckboxRecords().length;
+    if (selectCount === 0) {
+      proxy?.$modal.msgWarning('璇烽�夋嫨琛�');
+    } else {
+      await $table.removeCheckboxRow();
+      updateElement();
+    }
+  }
+};
+const updateElement = () => {
+  const $table = tableRef.value;
+  const data = $table.getTableData().fullData;
+  if (data.length) {
+    let extensionElements = props.element.businessObject.get('extensionElements');
+    if (!extensionElements) {
+      extensionElements = moddle.create('bpmn:ExtensionElements');
+    }
+    // 娓呴櫎鏃у��
+    extensionElements.values = extensionElements.values?.filter((item) => item.$type !== 'flowable:TaskListener') ?? [];
+    data.forEach((item) => {
+      const taskListener = moddle.create('flowable:TaskListener');
+      taskListener['event'] = item.event;
+      taskListener[item.type] = item.className;
+      if (item.params && item.params.length) {
+        item.params.forEach((field) => {
+          const fieldElement = moddle.create('flowable:Field');
+          fieldElement['name'] = field.name;
+          fieldElement[field.type] = field.value;
+          taskListener.get('fields').push(fieldElement);
+        });
+      }
+      extensionElements.get('values').push(taskListener);
+    });
+    updateProperties({ extensionElements: extensionElements });
+  } else {
+    const extensionElements = props.element.businessObject[`extensionElements`];
+    if (extensionElements) {
+      extensionElements.values = extensionElements.values?.filter((item) => item.$type !== 'flowable:TaskListener') ?? [];
+    }
+  }
+};
+
+const cellDBLClickEvent: VxeTableEvents.CellDblclick<TaskListenerVO> = ({ row }) => {
+  editEvent(row);
+};
+
+const menuConfig = reactive<VxeTablePropTypes.MenuConfig<TaskListenerVO>>({
+  body: {
+    options: [
+      [
+        { code: 'edit', name: '缂栬緫', prefixIcon: 'vxe-icon-edit', disabled: false },
+        { code: 'remove', name: '鍒犻櫎', prefixIcon: 'vxe-icon-delete', disabled: false }
+      ]
+    ]
+  },
+  visibleMethod({ options, column }) {
+    const isDisabled = !column;
+    options.forEach((list) => {
+      list.forEach((item) => {
+        item.disabled = isDisabled;
+      });
+    });
+    return true;
+  }
+});
+const contextMenuClickEvent: VxeTableEvents.MenuClick<TaskListenerVO> = ({ menu, row, column }) => {
+  const $table = tableRef.value;
+  if ($table) {
+    switch (menu.code) {
+      case 'edit':
+        editEvent(row);
+        break;
+      case 'remove':
+        removeEvent(row);
+        break;
+    }
+  }
+};
+const initTableData = () => {
+  tableData.value =
+    props.element.businessObject.extensionElements?.values
+      .filter((item) => item.$type === 'flowable:TaskListener')
+      .map((item) => {
+        let type;
+        if ('class' in item) type = 'class';
+        if ('expression' in item) type = 'expression';
+        if ('delegateExpression' in item) type = 'delegateExpression';
+        return {
+          event: item.event,
+          type: type,
+          className: item[type],
+          params:
+            item.fields?.map((field) => {
+              let fieldType;
+              if ('stringValue' in field) fieldType = 'stringValue';
+              if ('expression' in field) fieldType = 'expression';
+              return {
+                name: field.name,
+                type: fieldType,
+                value: field[fieldType]
+              };
+            }) ?? []
+        };
+      }) ?? [];
+};
+
+onMounted(() => {
+  initTableData();
+});
+
+const typeSelect = [
+  { id: '742fdeb7-23b4-416b-ac66-cd4ec8b901b7', label: '绫�', value: 'class' },
+  { id: '660c9c46-8fae-4bae-91a0-0335420019dc', label: '琛ㄨ揪寮�', value: 'expression' },
+  { id: '4b8135ab-6bc3-4a0f-80be-22f58bc6c5fd', label: '濮旀墭琛ㄨ揪寮�', value: 'delegateExpression' }
+];
+const eventSelect = [
+  { id: 'e6e0a51a-2d5d-4dc4-b847-b5c14f43a6ab', label: '鍒涘缓', value: 'create' },
+  { id: '6da97c1e-15fc-4445-8943-75d09f49778e', label: '鎸囨淳', value: 'assignment' },
+  { id: '6a2cbcec-e026-4f11-bef7-fff0b5c871e2', label: '瀹屾垚', value: 'complete' },
+  { id: '68801972-85f1-482f-bd86-1fad015c26ed', label: '鍒犻櫎', value: 'delete' }
+];
+</script>
+
+<style scoped lang="scss">
+.el-badge {
+  :deep(.el-badge__content) {
+    top: 10px;
+  }
+}
+</style>
diff --git a/src/components/BpmnDesign/index.vue b/src/components/BpmnDesign/index.vue
new file mode 100644
index 0000000..1f84516
--- /dev/null
+++ b/src/components/BpmnDesign/index.vue
@@ -0,0 +1,71 @@
+<template>
+  <div class="design">
+    <el-dialog v-model="visible" width="100%" fullscreen :title="title">
+      <div class="modeler">
+        <bpmn-design ref="bpmnDesignRef" @save-call-back="saveCallBack"></bpmn-design>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script lang="ts" setup name="Design">
+import { getInfo, editModelXml } from '@/api/workflow/model';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+import { ModelForm } from '@/api/workflow/model/types';
+import BpmnDesign from '@/bpmn/index.vue';
+import useDialog from '@/hooks/useDialog';
+const bpmnDesignRef = ref<InstanceType<typeof BpmnDesign>>();
+const modelForm = ref<ModelForm>();
+const emit = defineEmits(['closeCallBack']);
+const { visible, title } = useDialog({
+  title: '缂栬緫娴佺▼'
+});
+const modelId = ref('');
+const open = async (id) => {
+  visible.value = true;
+  modelId.value = id;
+  const { data } = await getInfo(id);
+  modelForm.value = data;
+  bpmnDesignRef.value.initDiagram(modelForm.value.xml);
+};
+//淇濆瓨妯″瀷
+const saveCallBack = async (data) => {
+  await proxy?.$modal.confirm('鏄惁纭淇濆瓨锛�');
+  data.loading.value = true;
+  modelForm.value.id = modelId.value;
+  modelForm.value.xml = data.xml;
+  modelForm.value.svg = data.svg;
+  modelForm.value.key = data.key;
+  modelForm.value.name = data.name;
+  editModelXml(modelForm.value).then((res) => {
+    if (res.code === 200) {
+      visible.value = false;
+      proxy?.$modal.msgSuccess('淇濆瓨鎴愬姛');
+      emit('closeCallBack', data);
+    }
+  });
+  data.loading.value = false;
+};
+
+/**
+ * 瀵瑰鏆撮湶瀛愮粍浠舵柟娉�
+ */
+defineExpose({
+  open
+});
+</script>
+
+<style lang="scss" scoped>
+.design {
+  :deep(.el-dialog .el-dialog__body) {
+    max-height: 100% !important;
+    min-height: calc(100vh - 80px);
+    padding: 10px 0 10px 0 !important;
+  }
+  :deep(.el-dialog__header) {
+    padding: 0 0 5px 0 !important;
+  }
+}
+</style>
diff --git a/src/components/BpmnView/index.vue b/src/components/BpmnView/index.vue
new file mode 100644
index 0000000..a79f084
--- /dev/null
+++ b/src/components/BpmnView/index.vue
@@ -0,0 +1,410 @@
+<template>
+  <div v-loading="loading" class="bpmnDialogContainers">
+    <el-header style="border-bottom: 1px solid rgb(218 218 218); height: auto">
+      <div class="header-div">
+        <div>
+          <el-tooltip effect="dark" content="鑷�傚簲灞忓箷" placement="bottom">
+            <el-button size="small" icon="Rank" @click="fitViewport" />
+          </el-tooltip>
+          <el-tooltip effect="dark" content="鏀惧ぇ" placement="bottom">
+            <el-button size="small" icon="ZoomIn" @click="zoomViewport(true)" />
+          </el-tooltip>
+          <el-tooltip effect="dark" content="缂╁皬" placement="bottom">
+            <el-button size="small" icon="ZoomOut" @click="zoomViewport(false)" />
+          </el-tooltip>
+        </div>
+        <div>
+          <div class="tips-label">
+            <div class="un-complete">鏈畬鎴�</div>
+            <div class="in-progress">杩涜涓�</div>
+            <div class="complete">宸插畬鎴�</div>
+          </div>
+        </div>
+      </div>
+    </el-header>
+    <div class="flow-containers">
+      <el-container class="bpmn-el-container" style="align-items: stretch">
+        <el-main style="padding: 0">
+          <div ref="canvas" class="canvas" />
+        </el-main>
+      </el-container>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import BpmnViewer from 'bpmn-js/lib/Viewer';
+import MoveCanvasModule from 'diagram-js/lib/navigation/movecanvas';
+import ZoomScrollModule from 'diagram-js/lib/navigation/zoomscroll';
+import { ModuleDeclaration } from 'didi';
+import { Canvas, ModdleElement } from 'bpmn';
+import EventBus from 'diagram-js/lib/core/EventBus';
+import Overlays from 'diagram-js/lib/features/overlays/Overlays';
+import processApi from '@/api/workflow/processInstance/index';
+
+const canvas = ref<HTMLElement>();
+const modeler = ref<BpmnViewer>();
+const taskList = ref([]);
+const zoom = ref(1);
+const xml = ref('');
+const loading = ref(false);
+const bpmnVisible = ref(true);
+const historyList = ref([]);
+
+const init = (instanceId) => {
+  loading.value = true;
+  bpmnVisible.value = true;
+  nextTick(async () => {
+    if (modeler.value) modeler.value.destroy();
+    modeler.value = new BpmnViewer({
+      container: canvas.value,
+      additionalModules: [
+        {
+          //绂佹婊氳疆婊氬姩
+          zoomScroll: ['value', '']
+        },
+        ZoomScrollModule,
+        MoveCanvasModule
+      ] as ModuleDeclaration[]
+    });
+    const resp = await processApi.getHistoryList(instanceId);
+    xml.value = resp.data.xml;
+    taskList.value = resp.data.taskList;
+    historyList.value = resp.data.historyList;
+    await createDiagram(xml.value);
+    loading.value = false;
+  });
+};
+
+const initXml = (xmlStr: string) => {
+  loading.value = true;
+  bpmnVisible.value = true;
+  nextTick(async () => {
+    if (modeler.value) modeler.value.destroy();
+    modeler.value = new BpmnViewer({
+      container: canvas.value,
+      additionalModules: [
+        {
+          //绂佹婊氳疆婊氬姩
+          zoomScroll: ['value', '']
+        },
+        ZoomScrollModule,
+        MoveCanvasModule
+      ] as ModuleDeclaration[]
+    });
+    xml.value = xmlStr;
+    await createDiagram(xml.value);
+    loading.value = false;
+  });
+};
+
+const createDiagram = async (data) => {
+  try {
+    await modeler.value.importXML(data);
+    fitViewport();
+    fillColor();
+    loading.value = false;
+    addEventBusListener();
+  } catch (err) {
+    console.log(err);
+  }
+};
+const addEventBusListener = () => {
+  const eventBus = modeler.value.get<EventBus>('eventBus');
+  const overlays = modeler.value.get<Overlays>('overlays');
+  eventBus.on<ModdleElement>('element.hover', (e) => {
+    let data = historyList.value.find((t) => t.taskDefinitionKey === e.element.id);
+    if (e.element.type === 'bpmn:UserTask' && data) {
+      setTimeout(() => {
+        genNodeDetailBox(e, overlays, data);
+      }, 10);
+    }
+  });
+  eventBus.on('element.out', (e) => {
+    overlays.clear();
+  });
+};
+const genNodeDetailBox = (e, overlays, data) => {
+  overlays.add(e.element.id, {
+    position: { top: e.element.height, left: 0 },
+    html: `<div class="verlays">
+                    <p>瀹℃壒浜哄憳: ${data.nickName || ''}<p/>
+                    <p>鑺傜偣鐘舵�侊細${data.status || ''}</p>
+                    <p>寮�濮嬫椂闂达細${data.startTime || ''}</p>
+                    <p>缁撴潫鏃堕棿锛�${data.endTime || ''}</p>
+                    <p>瀹℃壒鑰楁椂锛�${data.runDuration || ''}</p>
+                   </div>`
+  });
+};
+// 璁╁浘鑳借嚜閫傚簲灞忓箷
+const fitViewport = () => {
+  zoom.value = modeler.value.get<Canvas>('canvas').zoom('fit-viewport');
+  const bbox = document.querySelector<SVGGElement>('.flow-containers .viewport').getBBox();
+  const currentViewBox = modeler.value.get('canvas').viewbox();
+  const elementMid = {
+    x: bbox.x + bbox.width / 2 - 65,
+    y: bbox.y + bbox.height / 2
+  };
+  modeler.value.get<Canvas>('canvas').viewbox({
+    x: elementMid.x - currentViewBox.width / 2,
+    y: elementMid.y - currentViewBox.height / 2,
+    width: currentViewBox.width,
+    height: currentViewBox.height
+  });
+  zoom.value = (bbox.width / currentViewBox.width) * 1.8;
+};
+// 鏀惧ぇ缂╁皬
+const zoomViewport = (zoomIn = true) => {
+  zoom.value = modeler.value.get<Canvas>('canvas').zoom();
+  zoom.value += zoomIn ? 0.1 : -0.1;
+  modeler.value.get<Canvas>('canvas').zoom(zoom.value);
+};
+//涓婅壊
+const fillColor = () => {
+  const canvas = modeler.value.get<Canvas>('canvas');
+  bpmnNodeList(modeler.value._definitions.rootElements[0].flowElements, canvas);
+};
+//閫掑綊涓婅壊
+const bpmnNodeList = (flowElements, canvas) => {
+  flowElements.forEach((n) => {
+    if (n.$type === 'bpmn:UserTask') {
+      const completeTask = taskList.value.find((m) => m.key === n.id);
+      if (completeTask) {
+        canvas.addMarker(n.id, completeTask.completed ? 'highlight' : 'highlight-todo');
+        n.outgoing?.forEach((nn) => {
+          const targetTask = taskList.value.find((m) => m.key === nn.targetRef.id);
+          if (targetTask) {
+            canvas.addMarker(nn.id, targetTask.completed ? 'highlight' : 'highlight-todo');
+          } else if (nn.targetRef.$type === 'bpmn:ExclusiveGateway') {
+            canvas.addMarker(nn.id, completeTask.completed ? 'highlight' : 'highlight-todo');
+            canvas.addMarker(nn.targetRef.id, completeTask.completed ? 'highlight' : 'highlight-todo');
+            nn.targetRef.outgoing.forEach((e) => {
+              gateway(e.id, e.targetRef.$type, e.targetRef.id, canvas, completeTask.completed);
+            });
+          } else if (nn.targetRef.$type === 'bpmn:ParallelGateway') {
+            canvas.addMarker(nn.id, completeTask.completed ? 'highlight' : 'highlight-todo');
+            canvas.addMarker(nn.targetRef.id, completeTask.completed ? 'highlight' : 'highlight-todo');
+            nn.targetRef.outgoing.forEach((e) => {
+              gateway(e.id, e.targetRef.$type, e.targetRef.id, canvas, completeTask.completed);
+            });
+          } else if (nn.targetRef.$type === 'bpmn:InclusiveGateway') {
+            canvas.addMarker(nn.id, completeTask.completed ? 'highlight' : 'highlight-todo');
+            canvas.addMarker(nn.targetRef.id, completeTask.completed ? 'highlight' : 'highlight-todo');
+            nn.targetRef.outgoing.forEach((e) => {
+              gateway(e.id, e.targetRef.$type, e.targetRef.id, canvas, completeTask.completed);
+            });
+          }
+        });
+      }
+    } else if (n.$type === 'bpmn:ExclusiveGateway') {
+      n.outgoing.forEach((nn) => {
+        const targetTask = taskList.value.find((m) => m.key === nn.targetRef.id);
+        if (targetTask) {
+          canvas.addMarker(nn.id, targetTask.completed ? 'highlight' : 'highlight-todo');
+        }
+      });
+    } else if (n.$type === 'bpmn:ParallelGateway') {
+      n.outgoing.forEach((nn) => {
+        const targetTask = taskList.value.find((m) => m.key === nn.targetRef.id);
+        if (targetTask) {
+          canvas.addMarker(nn.id, targetTask.completed ? 'highlight' : 'highlight-todo');
+        }
+      });
+    } else if (n.$type === 'bpmn:InclusiveGateway') {
+      n.outgoing.forEach((nn) => {
+        const targetTask = taskList.value.find((m) => m.key === nn.targetRef.id);
+        if (targetTask) {
+          canvas.addMarker(nn.id, targetTask.completed ? 'highlight' : 'highlight-todo');
+        }
+      });
+    } else if (n.$type === 'bpmn:SubProcess') {
+      const completeTask = taskList.value.find((m) => m.key === n.id);
+      if (completeTask) {
+        canvas.addMarker(n.id, completeTask.completed ? 'highlight' : 'highlight-todo');
+      }
+      bpmnNodeList(n.flowElements, canvas);
+    } else if (n.$type === 'bpmn:StartEvent') {
+      canvas.addMarker(n.id, 'startEvent');
+      if (n.outgoing) {
+        n.outgoing.forEach((nn) => {
+          const completeTask = taskList.value.find((m) => m.key === nn.targetRef.id);
+          if (completeTask) {
+            canvas.addMarker(nn.id, 'highlight');
+            canvas.addMarker(n.id, 'highlight');
+          }
+        });
+      }
+    } else if (n.$type === 'bpmn:EndEvent') {
+      canvas.addMarker(n.id, 'endEvent');
+      const completeTask = taskList.value.find((m) => m.key === n.id);
+      if (completeTask) {
+        canvas.addMarker(completeTask.key, 'highlight');
+        canvas.addMarker(n.id, 'highlight');
+        return;
+      }
+    }
+  });
+};
+const gateway = (id, targetRefType, targetRefId, canvas, completed) => {
+  if (targetRefType === 'bpmn:ExclusiveGateway') {
+    canvas.addMarker(id, completed ? 'highlight' : 'highlight-todo');
+    canvas.addMarker(targetRefId, completed ? 'highlight' : 'highlight-todo');
+  }
+  if (targetRefType === 'bpmn:ParallelGateway') {
+    canvas.addMarker(id, completed ? 'highlight' : 'highlight-todo');
+    canvas.addMarker(targetRefId, completed ? 'highlight' : 'highlight-todo');
+  }
+  if (targetRefType === 'bpmn:InclusiveGateway') {
+    canvas.addMarker(id, completed ? 'highlight' : 'highlight-todo');
+    canvas.addMarker(targetRefId, completed ? 'highlight' : 'highlight-todo');
+  }
+};
+defineExpose({
+  init,
+  initXml
+});
+</script>
+
+<style lang="scss" scoped>
+.canvas {
+  width: 100%;
+  height: 100%;
+}
+
+.header-div {
+  display: flex;
+  padding: 10px 0;
+  justify-content: space-between;
+
+  .tips-label {
+    display: flex;
+    div {
+      margin-right: 10px;
+      padding: 5px;
+      font-size: 12px;
+    }
+    .un-complete {
+      border: 1px solid #000;
+    }
+    .in-progress {
+      background-color: rgb(255, 237, 204);
+      border: 1px dashed orange;
+    }
+    .complete {
+      background-color: rgb(204, 230, 204);
+      border: 1px solid green;
+    }
+  }
+}
+
+.view-mode {
+  .el-header,
+  .el-aside,
+  .djs-palette,
+  .bjs-powered-by {
+    display: none;
+  }
+  .el-loading-mask {
+    background-color: initial;
+  }
+  .el-loading-spinner {
+    display: none;
+  }
+}
+.bpmn-el-container {
+  height: calc(100vh - 350px);
+}
+.flow-containers {
+  width: 100%;
+  height: 100%;
+  overflow-y: auto;
+  .canvas {
+    width: 100%;
+    height: 100%;
+  }
+  .load {
+    margin-right: 10px;
+  }
+  :deep(.el-form-item__label) {
+    font-size: 13px;
+  }
+
+  :deep(.djs-palette) {
+    left: 0 !important;
+    top: 0;
+    border-top: none;
+  }
+
+  :deep(.djs-container svg) {
+    min-height: 650px;
+  }
+
+  :deep(.startEvent.djs-shape .djs-visual > :nth-child(1)) {
+    fill: #77df6d !important;
+  }
+  :deep(.endEvent.djs-shape .djs-visual > :nth-child(1)) {
+    fill: #ee7b77 !important;
+  }
+  :deep(.highlight.djs-shape .djs-visual > :nth-child(1)) {
+    fill: green !important;
+    stroke: green !important;
+    fill-opacity: 0.2 !important;
+  }
+  :deep(.highlight.djs-shape .djs-visual > :nth-child(2)) {
+    fill: green !important;
+  }
+  :deep(.highlight.djs-shape .djs-visual > path) {
+    fill: green !important;
+    fill-opacity: 0.2 !important;
+    stroke: green !important;
+  }
+  :deep(.highlight.djs-connection > .djs-visual > path) {
+    stroke: green !important;
+  }
+
+  // 杈规婊氬姩鍔ㄧ敾
+  @keyframes path-animation {
+    from {
+      stroke-dashoffset: 100%;
+    }
+
+    to {
+      stroke-dashoffset: 0%;
+    }
+  }
+
+  :deep(.highlight-todo.djs-connection > .djs-visual > path) {
+    animation: path-animation 60s;
+    animation-timing-function: linear;
+    animation-iteration-count: infinite;
+    stroke-dasharray: 4px !important;
+    stroke: orange !important;
+    fill-opacity: 0.2 !important;
+    marker-end: url('#sequenceflow-end-_E7DFDF-_E7DFDF-803g1kf6zwzmcig1y2ulm5egr');
+  }
+
+  :deep(.highlight-todo.djs-shape .djs-visual > :nth-child(1)) {
+    animation: path-animation 60s;
+    animation-timing-function: linear;
+    animation-iteration-count: infinite;
+    stroke-dasharray: 4px !important;
+    stroke: orange !important;
+    fill: orange !important;
+    fill-opacity: 0.2 !important;
+  }
+}
+:deep(.verlays) {
+  width: 250px;
+  background: rgb(102, 102, 102);
+  border-radius: 4px;
+  border: 1px solid #ebeef5;
+  color: #fff;
+  padding: 15px 10px;
+  p {
+    line-height: 28px;
+    margin: 0;
+    padding: 0;
+  }
+  cursor: pointer;
+}
+</style>
diff --git a/src/components/Breadcrumb/index.vue b/src/components/Breadcrumb/index.vue
index b96829a..9502e60 100644
--- a/src/components/Breadcrumb/index.vue
+++ b/src/components/Breadcrumb/index.vue
@@ -2,8 +2,7 @@
   <el-breadcrumb class="app-breadcrumb" separator="/">
     <transition-group name="breadcrumb">
       <el-breadcrumb-item v-for="(item, index) in levelList" :key="item.path">
-        <span v-if="item.redirect === 'noRedirect' || index == levelList.length - 1" class="no-redirect">{{
-          item.meta?.title }}</span>
+        <span v-if="item.redirect === 'noRedirect' || index == levelList.length - 1" class="no-redirect">{{ item.meta?.title }}</span>
         <a v-else @click.prevent="handleLink(item)">{{ item.meta?.title }}</a>
       </el-breadcrumb-item>
     </transition-group>
@@ -11,42 +10,42 @@
 </template>
 
 <script setup lang="ts">
-import { RouteLocationMatched } from 'vue-router'
+import { RouteLocationMatched } from 'vue-router';
 
 const route = useRoute();
 const router = useRouter();
-const levelList = ref<RouteLocationMatched[]>([])
+const levelList = ref<RouteLocationMatched[]>([]);
 
 const getBreadcrumb = () => {
   // only show routes with meta.title
-  let matched = route.matched.filter(item => item.meta && item.meta.title);
-  const first = matched[0]
+  let matched = route.matched.filter((item) => item.meta && item.meta.title);
+  const first = matched[0];
   // 鍒ゆ柇鏄惁涓洪椤�
   if (!isDashboard(first)) {
-    matched = ([{ path: '/index', meta: { title: '棣栭〉' } }] as any).concat(matched)
+    matched = ([{ path: '/index', meta: { title: '棣栭〉' } }] as any).concat(matched);
   }
-  levelList.value = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false)
-}
+  levelList.value = matched.filter((item) => item.meta && item.meta.title && item.meta.breadcrumb !== false);
+};
 const isDashboard = (route: RouteLocationMatched) => {
-  const name = route && route.name as string
+  const name = route && (route.name as string);
   if (!name) {
-    return false
+    return false;
   }
-  return name.trim() === 'Index'
-}
-const handleLink = (item: RouteLocationMatched) => {
-  const { redirect, path } = item
-  redirect ? router.push(redirect as string) : router.push(path)
-}
+  return name.trim() === 'Index';
+};
+const handleLink = (item) => {
+  const { redirect, path } = item;
+  redirect ? router.push(redirect) : router.push(path);
+};
 
 watchEffect(() => {
   // if you go to the redirect page, do not update the breadcrumbs
-  if (route.path.startsWith('/redirect/')) return
-  getBreadcrumb()
-})
+  if (route.path.startsWith('/redirect/')) return;
+  getBreadcrumb();
+});
 onMounted(() => {
   getBreadcrumb();
-})
+});
 </script>
 
 <style lang="scss" scoped>
diff --git a/src/components/BuildCode/index.vue b/src/components/BuildCode/index.vue
index b23665c..0b8e3f4 100644
--- a/src/components/BuildCode/index.vue
+++ b/src/components/BuildCode/index.vue
@@ -1,53 +1,50 @@
-<!-- 浠g爜鏋勫缓 -->
-<script setup lang="ts">
-
-const props = defineProps({
-  showBtn: {
-    type: Boolean,
-    default: false
-  },
-  formJson: {
-    type: Object,
-    default: undefined
-  }
-})
-const { proxy } = getCurrentInstance() as ComponentInternalInstance;
-const buildRef = ref();
-const emits = defineEmits(['reJson', 'saveDesign']);
-
-
-
-//鑾峰彇琛ㄥ崟json
-const getJson = () => {
-  const formJson = JSON.stringify(buildRef.value.getFormJson())
-  const fieldJson = JSON.stringify(buildRef.value.getFieldWidgets())
-  let data = {
-    formJson, fieldJson
-  }
-  emits("saveDesign", data)
-}
-
-onMounted(() => {
-  if (props.formJson) {
-    buildRef.value.setFormJson(props.formJson)
-  }
-})
-</script>
-
 <template>
+  <!-- 浠g爜鏋勫缓 -->
   <div>
     <v-form-designer
-      class="build"
       ref="buildRef"
+      class="build"
       :designer-config="{ importJsonButton: true, exportJsonButton: true, exportCodeButton: true, generateSFCButton: true, formTemplates: true }"
     >
-      <template #customToolButtons v-if="showBtn">
+      <template v-if="showBtn" #customToolButtons>
         <el-button link type="primary" icon="Select" @click="getJson">淇濆瓨</el-button>
       </template>
     </v-form-designer>
   </div>
 </template>
 
+<script setup lang="ts">
+interface Props {
+  showBtn: boolean;
+  formJson: any;
+}
+
+const props = withDefaults(defineProps<Props>(), {
+  showBtn: true,
+  formJson: ''
+});
+
+const buildRef = ref();
+const emits = defineEmits(['reJson', 'saveDesign']);
+
+//鑾峰彇琛ㄥ崟json
+const getJson = () => {
+  const formJson = JSON.stringify(buildRef.value.getFormJson());
+  const fieldJson = JSON.stringify(buildRef.value.getFieldWidgets());
+  let data = {
+    formJson,
+    fieldJson
+  };
+  emits('saveDesign', data);
+};
+
+onMounted(() => {
+  if (props.formJson) {
+    buildRef.value.setFormJson(props.formJson);
+  }
+});
+</script>
+
 <style lang="scss">
 .build {
   margin: 0 !important;
diff --git a/src/components/BuildCode/render.vue b/src/components/BuildCode/render.vue
index 62f3d86..aeb9312 100644
--- a/src/components/BuildCode/render.vue
+++ b/src/components/BuildCode/render.vue
@@ -1,62 +1,57 @@
+<template>
+  <div class="">
+    <v-form-render ref="vFormRef" :form-json="formJson" :form-data="formData" />
+  </div>
+</template>
+
 <!-- 鍔ㄦ�佽〃鍗曟覆鏌� -->
-<script setup name="Render">
+<script setup name="Render" lang="ts">
+interface Props {
+  formJson: string | object;
+  formData: string | object;
+  isView: boolean;
+}
 
-const props = defineProps({
-  formJson: {
-    type: [String, Object],
-    default: ""
-  },
-  formData: {
-    type: [String, Object],
-    default: ""
-  },
-  isView: {
-    type: Boolean,
-    default: false
-  }
-})
+const props = withDefaults(defineProps<Props>(), {
+  formJson: '',
+  formData: '',
+  isView: false
+});
 
-const vFormRef = ref(null)
+const vFormRef = ref();
 // 鑾峰彇琛ㄥ崟鏁版嵁-寮傛
 const getFormData = () => {
-  return vFormRef.value.getFormData()
-}
+  return vFormRef.value.getFormData();
+};
 
 /**
  * 璁剧疆琛ㄥ崟鍐呭
  * @param {琛ㄥ崟閰嶇疆} formConf
  * formConfig锛歿 formTemplate锛氳〃鍗曟ā鏉匡紝formData锛氳〃鍗曟暟鎹紝hiddenField锛氶渶瑕侀殣钘忕殑瀛楁瀛楃涓查泦鍚堬紝disabledField锛氶渶瑕佺鐢ㄧ殑鑷瀛楃涓查泦鍚坿
  */
-const initForm = (formConf) => {
-  const { formTemplate, formData, hiddenField, disabledField } = toRaw(formConf)
+const initForm = (formConf: any) => {
+  const { formTemplate, formData, hiddenField, disabledField } = toRaw(formConf);
   if (formTemplate) {
-    vFormRef.value.setFormJson(formTemplate)
+    vFormRef.value.setFormJson(formTemplate);
     if (formData) {
-      vFormRef.value.setFormData(formData)
+      vFormRef.value.setFormData(formData);
     }
     if (disabledField && disabledField.length > 0) {
       setTimeout(() => {
-        vFormRef.value.disableWidgets(disabledField)
-      }, 200)
+        vFormRef.value.disableWidgets(disabledField);
+      }, 200);
     }
     if (hiddenField && hiddenField.length > 0) {
       setTimeout(() => {
-        vFormRef.value.hideWidgets(hiddenField)
-      }, 200)
+        vFormRef.value.hideWidgets(hiddenField);
+      }, 200);
     }
     if (props.isView) {
-      console.log(props.isView)
       setTimeout(() => {
-        vFormRef.value.disableForm()
-      }, 100)
+        vFormRef.value.disableForm();
+      }, 100);
     }
   }
-}
-defineExpose({ getFormData, initForm })
+};
+defineExpose({ getFormData, initForm });
 </script>
-
-<template>
-  <div class="">
-    <v-form-render ref="vFormRef" :form-json="formJson" :form-data="formData" />
-  </div>
-</template>
diff --git a/src/components/DictTag/index.vue b/src/components/DictTag/index.vue
index 45b288b..ad326c2 100644
--- a/src/components/DictTag/index.vue
+++ b/src/components/DictTag/index.vue
@@ -2,19 +2,31 @@
   <div>
     <template v-for="(item, index) in options">
       <template v-if="values.includes(item.value)">
-        <span v-if="(item.elTagType === 'default' || item.elTagType === '') && (item.elTagClass === '' || item.elTagClass == null)"
-              :key="item.value" :index="index" :class="item.elTagClass">
-          {{ item.label + " " }}
+        <span
+          v-if="(item.elTagType === 'default' || item.elTagType === '') && (item.elTagClass === '' || item.elTagClass == null)"
+          :key="item.value"
+          :index="index"
+          :class="item.elTagClass"
+        >
+          {{ item.label + ' ' }}
         </span>
         <el-tag
           v-else
-          :disable-transitions="true"
           :key="item.value + ''"
+          :disable-transitions="true"
           :index="index"
-          :type="(item.elTagType === 'primary' || item.elTagType === 'default')? '' : item.elTagType"
+          :type="
+            item.elTagType === 'primary' ||
+            item.elTagType === 'success' ||
+            item.elTagType === 'info' ||
+            item.elTagType === 'warning' ||
+            item.elTagType === 'danger'
+              ? item.elTagType
+              : 'primary'
+          "
           :class="item.elTagClass"
         >
-          {{ item.label + " " }}
+          {{ item.label + ' ' }}
         </el-tag>
       </template>
     </template>
@@ -25,57 +37,54 @@
 </template>
 
 <script setup lang="ts">
-import { propTypes } from '@/utils/propTypes';
-
-
-const props = defineProps({
-  // 鏁版嵁
-  options: {
-    type: Array as PropType<DictDataOption[]>,
-    default: null,
-  },
-  // 褰撳墠鐨勫��
-  value: [Number, String, Array] as PropType<number | string | Array<number | string>>,
-  // 褰撴湭鎵惧埌鍖归厤鐨勬暟鎹椂锛屾樉绀簐alue
-  showValue: propTypes.bool.def(true),
-  separator: propTypes.string.def(","),
+interface Props {
+  options: Array<DictDataOption>;
+  value: number | string | Array<number | string>;
+  showValue?: boolean;
+  separator?: string;
+}
+const props = withDefaults(defineProps<Props>(), {
+  showValue: true,
+  separator: ','
 });
+
 const values = computed(() => {
-  if (props.value === '' || props.value === null || typeof props.value === "undefined") return []
-  return Array.isArray(props.value) ? props.value.map(item => '' + item) : String(props.value).split(props.separator);
+  if (props.value === '' || props.value === null || typeof props.value === 'undefined') return [];
+  return Array.isArray(props.value) ? props.value.map((item) => '' + item) : String(props.value).split(props.separator);
 });
 
 const unmatch = computed(() => {
-  if (props.options?.length == 0 || props.value === '' || props.value === null || typeof props.value === "undefined") return false
+  if (props.options?.length == 0 || props.value === '' || props.value === null || typeof props.value === 'undefined') return false;
   // 浼犲叆鍊间负闈炴暟缁�
-  values.value.forEach(item => {
-    if (!props.options.some(v => v.value === item)) {
-      return true // 濡傛灉鏈夋湭鍖归厤椤癸紝灏嗘爣蹇楄缃负true
+  let unmatch = false; // 娣诲姞涓�涓爣蹇楁潵鍒ゆ柇鏄惁鏈夋湭鍖归厤椤�
+  values.value.forEach((item) => {
+    if (!props.options.some((v) => v.value === item)) {
+      unmatch = true; // 濡傛灉鏈夋湭鍖归厤椤癸紝灏嗘爣蹇楄缃负true
     }
-  })
-  return false // 杩斿洖鏍囧織鐨勫��
+  });
+  return unmatch; // 杩斿洖鏍囧織鐨勫��
 });
 
 const unmatchArray = computed(() => {
-// 璁板綍鏈尮閰嶇殑椤�
+  // 璁板綍鏈尮閰嶇殑椤�
   const itemUnmatchArray: Array<string | number> = [];
-  if (props.value !== '' && props.value !== null && typeof props.value !== "undefined") {
-    values.value.forEach(item => {
-      if (!props.options.some(v => v.value === item)) {
+  if (props.value !== '' && props.value !== null && typeof props.value !== 'undefined') {
+    values.value.forEach((item) => {
+      if (!props.options.some((v) => v.value === item)) {
         itemUnmatchArray.push(item);
       }
-    })
+    });
   }
   // 娌℃湁value涓嶆樉绀�
   return handleArray(itemUnmatchArray);
 });
 
 const handleArray = (array: Array<string | number>) => {
-  if (array.length === 0) return "";
+  if (array.length === 0) return '';
   return array.reduce((pre, cur) => {
-    return pre + " " + cur;
+    return pre + ' ' + cur;
   });
-}
+};
 </script>
 
 <style scoped>
diff --git a/src/components/Editor/index.vue b/src/components/Editor/index.vue
index b5299b4..fc30c85 100644
--- a/src/components/Editor/index.vue
+++ b/src/components/Editor/index.vue
@@ -1,6 +1,7 @@
 <template>
   <div>
     <el-upload
+      v-if="type === 'url'"
       :action="upload.url"
       :before-upload="handleBeforeUpload"
       :on-success="handleUploadSuccess"
@@ -9,28 +10,30 @@
       name="file"
       :show-file-list="false"
       :headers="upload.headers"
-      ref="uploadRef"
-      v-if="type === 'url'"
     >
+      <i ref="uploadRef"></i>
     </el-upload>
-    <div class="editor">
-      <quill-editor
-        ref="quillEditorRef"
-        v-model:content="content"
-        contentType="html"
-        @textChange="(e: any) => $emit('update:modelValue', content)"
-        :options="options"
-        :style="styles"
-      />
-    </div>
+  </div>
+  <div class="editor">
+    <quill-editor
+      ref="quillEditorRef"
+      v-model:content="content"
+      content-type="html"
+      :options="options"
+      :style="styles"
+      @text-change="(e: any) => $emit('update:modelValue', content)"
+    />
   </div>
 </template>
 
 <script setup lang="ts">
-import { QuillEditor, Quill } from '@vueup/vue-quill';
 import '@vueup/vue-quill/dist/vue-quill.snow.css';
+
+import { QuillEditor, Quill } from '@vueup/vue-quill';
 import { propTypes } from '@/utils/propTypes';
-import { globalHeaders } from "@/utils/request";
+import { globalHeaders } from '@/utils/request';
+
+defineEmits(['update:modelValue']);
 
 const props = defineProps({
   /* 缂栬緫鍣ㄧ殑鍐呭 */
@@ -52,42 +55,43 @@
 const upload = reactive<UploadOption>({
   headers: globalHeaders(),
   url: import.meta.env.VITE_APP_BASE_API + '/resource/oss/upload'
-})
+});
 const quillEditorRef = ref();
+const uploadRef = ref<HTMLDivElement>();
 
-const options = ref({
-  theme: "snow",
+const options = ref<any>({
+  theme: 'snow',
   bounds: document.body,
-  debug: "warn",
+  debug: 'warn',
   modules: {
     // 宸ュ叿鏍忛厤缃�
     toolbar: {
       container: [
-        ["bold", "italic", "underline", "strike"],       // 鍔犵矖 鏂滀綋 涓嬪垝绾� 鍒犻櫎绾�
-        ["blockquote", "code-block"],                    // 寮曠敤  浠g爜鍧�
-        [{ list: "ordered" }, { list: "bullet" }],       // 鏈夊簭銆佹棤搴忓垪琛�
-        [{ indent: "-1" }, { indent: "+1" }],            // 缂╄繘
-        [{ size: ["small", false, "large", "huge"] }],   // 瀛椾綋澶у皬
-        [{ header: [1, 2, 3, 4, 5, 6, false] }],         // 鏍囬
-        [{ color: [] }, { background: [] }],             // 瀛椾綋棰滆壊銆佸瓧浣撹儗鏅鑹�
-        [{ align: [] }],                                 // 瀵归綈鏂瑰紡
-        ["clean"],                                       // 娓呴櫎鏂囨湰鏍煎紡
-        ["link", "image", "video"]                       // 閾炬帴銆佸浘鐗囥�佽棰�
+        ['bold', 'italic', 'underline', 'strike'], // 鍔犵矖 鏂滀綋 涓嬪垝绾� 鍒犻櫎绾�
+        ['blockquote', 'code-block'], // 寮曠敤  浠g爜鍧�
+        [{ list: 'ordered' }, { list: 'bullet' }], // 鏈夊簭銆佹棤搴忓垪琛�
+        [{ indent: '-1' }, { indent: '+1' }], // 缂╄繘
+        [{ size: ['small', false, 'large', 'huge'] }], // 瀛椾綋澶у皬
+        [{ header: [1, 2, 3, 4, 5, 6, false] }], // 鏍囬
+        [{ color: [] }, { background: [] }], // 瀛椾綋棰滆壊銆佸瓧浣撹儗鏅鑹�
+        [{ align: [] }], // 瀵归綈鏂瑰紡
+        ['clean'], // 娓呴櫎鏂囨湰鏍煎紡
+        ['link', 'image', 'video'] // 閾炬帴銆佸浘鐗囥�佽棰�
       ],
       handlers: {
-        image: function (value: any) {
+        image: (value: boolean) => {
           if (value) {
             // 璋冪敤element鍥剧墖涓婁紶
-            (document.querySelector(".editor-img-uploader>.el-upload") as HTMLDivElement)?.click();
+            uploadRef.value.click();
           } else {
-            Quill.format("image", true);
+            Quill.format('image', true);
           }
-        },
-      },
+        }
+      }
     }
   },
-  placeholder: "璇疯緭鍏ュ唴瀹�",
-  readOnly: props.readOnly,
+  placeholder: '璇疯緭鍏ュ唴瀹�',
+  readOnly: props.readOnly
 });
 
 const styles = computed(() => {
@@ -99,14 +103,18 @@
     style.height = `${props.height}px`;
   }
   return style;
-})
+});
 
-const content = ref("");
-watch(() => props.modelValue, (v) => {
-  if (v !== content.value) {
-    content.value = v === undefined ? "<p></p>" : v;
-  }
-}, { immediate: true });
+const content = ref('');
+watch(
+  () => props.modelValue,
+  (v: string) => {
+    if (v !== content.value) {
+      content.value = v === undefined ? '<p></p>' : v;
+    }
+  },
+  { immediate: true }
+);
 
 // 鍥剧墖涓婁紶鎴愬姛杩斿洖鍥剧墖鍦板潃
 const handleUploadSuccess = (res: any) => {
@@ -117,19 +125,19 @@
     // 鑾峰彇鍏夋爣浣嶇疆
     let length = quill.selection.savedRange.index;
     // 鎻掑叆鍥剧墖锛宺es涓烘湇鍔″櫒杩斿洖鐨勫浘鐗囬摼鎺ュ湴鍧�
-    quill.insertEmbed(length, "image", res.data.url);
+    quill.insertEmbed(length, 'image', res.data.url);
     // 璋冩暣鍏夋爣鍒版渶鍚�
     quill.setSelection(length + 1);
     proxy?.$modal.closeLoading();
   } else {
-    proxy?.$modal.loading(res.msg);
+    proxy?.$modal.msgError('鍥剧墖鎻掑叆澶辫触');
     proxy?.$modal.closeLoading();
   }
-}
+};
 
 // 鍥剧墖涓婁紶鍓嶆嫤鎴�
 const handleBeforeUpload = (file: any) => {
-  const type = ["image/jpeg", "image/jpg", "image/png", "image/svg"];
+  const type = ['image/jpeg', 'image/jpg', 'image/png', 'image/svg'];
   const isJPG = type.includes(file.type);
   //妫�楠屾枃浠舵牸寮�
   if (!isJPG) {
@@ -146,13 +154,12 @@
   }
   proxy?.$modal.loading('姝e湪涓婁紶鏂囦欢锛岃绋嶅��...');
   return true;
-}
+};
 
 // 鍥剧墖澶辫触鎷︽埅
 const handleUploadError = (err: any) => {
-  console.error(err);
   proxy?.$modal.msgError('涓婁紶鏂囦欢澶辫触');
-}
+};
 </script>
 
 <style>
@@ -167,71 +174,71 @@
 .quill-img {
   display: none;
 }
-.ql-snow .ql-tooltip[data-mode="link"]::before {
-  content: "璇疯緭鍏ラ摼鎺ュ湴鍧�:";
+.ql-snow .ql-tooltip[data-mode='link']::before {
+  content: '璇疯緭鍏ラ摼鎺ュ湴鍧�:';
 }
 .ql-snow .ql-tooltip.ql-editing a.ql-action::after {
   border-right: 0;
-  content: "淇濆瓨";
+  content: '淇濆瓨';
   padding-right: 0;
 }
-.ql-snow .ql-tooltip[data-mode="video"]::before {
-  content: "璇疯緭鍏ヨ棰戝湴鍧�:";
+.ql-snow .ql-tooltip[data-mode='video']::before {
+  content: '璇疯緭鍏ヨ棰戝湴鍧�:';
 }
 .ql-snow .ql-picker.ql-size .ql-picker-label::before,
 .ql-snow .ql-picker.ql-size .ql-picker-item::before {
-  content: "14px";
+  content: '14px';
 }
-.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="small"]::before,
-.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="small"]::before {
-  content: "10px";
+.ql-snow .ql-picker.ql-size .ql-picker-label[data-value='small']::before,
+.ql-snow .ql-picker.ql-size .ql-picker-item[data-value='small']::before {
+  content: '10px';
 }
-.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="large"]::before,
-.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="large"]::before {
-  content: "18px";
+.ql-snow .ql-picker.ql-size .ql-picker-label[data-value='large']::before,
+.ql-snow .ql-picker.ql-size .ql-picker-item[data-value='large']::before {
+  content: '18px';
 }
-.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="huge"]::before,
-.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="huge"]::before {
-  content: "32px";
+.ql-snow .ql-picker.ql-size .ql-picker-label[data-value='huge']::before,
+.ql-snow .ql-picker.ql-size .ql-picker-item[data-value='huge']::before {
+  content: '32px';
 }
 .ql-snow .ql-picker.ql-header .ql-picker-label::before,
 .ql-snow .ql-picker.ql-header .ql-picker-item::before {
-  content: "鏂囨湰";
+  content: '鏂囨湰';
 }
-.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before,
-.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
-  content: "鏍囬1";
+.ql-snow .ql-picker.ql-header .ql-picker-label[data-value='1']::before,
+.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='1']::before {
+  content: '鏍囬1';
 }
-.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before,
-.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
-  content: "鏍囬2";
+.ql-snow .ql-picker.ql-header .ql-picker-label[data-value='2']::before,
+.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='2']::before {
+  content: '鏍囬2';
 }
-.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before,
-.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
-  content: "鏍囬3";
+.ql-snow .ql-picker.ql-header .ql-picker-label[data-value='3']::before,
+.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='3']::before {
+  content: '鏍囬3';
 }
-.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before,
-.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
-  content: "鏍囬4";
+.ql-snow .ql-picker.ql-header .ql-picker-label[data-value='4']::before,
+.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='4']::before {
+  content: '鏍囬4';
 }
-.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before,
-.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
-  content: "鏍囬5";
+.ql-snow .ql-picker.ql-header .ql-picker-label[data-value='5']::before,
+.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='5']::before {
+  content: '鏍囬5';
 }
-.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before,
-.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
-  content: "鏍囬6";
+.ql-snow .ql-picker.ql-header .ql-picker-label[data-value='6']::before,
+.ql-snow .ql-picker.ql-header .ql-picker-item[data-value='6']::before {
+  content: '鏍囬6';
 }
 .ql-snow .ql-picker.ql-font .ql-picker-label::before,
 .ql-snow .ql-picker.ql-font .ql-picker-item::before {
-  content: "鏍囧噯瀛椾綋";
+  content: '鏍囧噯瀛椾綋';
 }
-.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="serif"]::before,
-.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="serif"]::before {
-  content: "琛嚎瀛椾綋";
+.ql-snow .ql-picker.ql-font .ql-picker-label[data-value='serif']::before,
+.ql-snow .ql-picker.ql-font .ql-picker-item[data-value='serif']::before {
+  content: '琛嚎瀛椾綋';
 }
-.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="monospace"]::before,
-.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="monospace"]::before {
-  content: "绛夊瀛椾綋";
+.ql-snow .ql-picker.ql-font .ql-picker-label[data-value='monospace']::before,
+.ql-snow .ql-picker.ql-font .ql-picker-item[data-value='monospace']::before {
+  content: '绛夊瀛椾綋';
 }
 </style>
diff --git a/src/components/FileUpload/index.vue b/src/components/FileUpload/index.vue
index be99ac0..aaaa45c 100644
--- a/src/components/FileUpload/index.vue
+++ b/src/components/FileUpload/index.vue
@@ -1,6 +1,7 @@
 <template>
   <div class="upload-file">
     <el-upload
+      ref="fileUploadRef"
       multiple
       :action="uploadFileUrl"
       :before-upload="handleBeforeUpload"
@@ -12,30 +13,29 @@
       :show-file-list="false"
       :headers="headers"
       class="upload-file-uploader"
-      ref="fileUploadRef"
     >
       <!-- 涓婁紶鎸夐挳 -->
       <el-button type="primary">閫夊彇鏂囦欢</el-button>
     </el-upload>
     <!-- 涓婁紶鎻愮ず -->
-    <div class="el-upload__tip" v-if="showTip">
+    <div v-if="showTip" class="el-upload__tip">
       璇蜂笂浼�
       <template v-if="fileSize">
         澶у皬涓嶈秴杩� <b style="color: #f56c6c">{{ fileSize }}MB</b>
       </template>
       <template v-if="fileType">
-        鏍煎紡涓� <b style="color: #f56c6c">{{ fileType.join("/") }}</b>
+        鏍煎紡涓� <b style="color: #f56c6c">{{ fileType.join('/') }}</b>
       </template>
       鐨勬枃浠�
     </div>
     <!-- 鏂囦欢鍒楄〃 -->
     <transition-group class="upload-file-list el-upload-list el-upload-list--text" name="el-fade-in-linear" tag="ul">
-      <li :key="file.uid" class="el-upload-list__item ele-upload-list__item-content" v-for="(file, index) in fileList">
+      <li v-for="(file, index) in fileList" :key="file.uid" class="el-upload-list__item ele-upload-list__item-content">
         <el-link :href="`${file.url}`" :underline="false" target="_blank">
           <span class="el-icon-document"> {{ getFileName(file.name) }} </span>
         </el-link>
         <div class="ele-upload-list__item-content-action">
-          <el-link :underline="false" @click="handleDelete(index)" type="danger">鍒犻櫎</el-link>
+          <el-button type="danger" link @click="handleDelete(index)">鍒犻櫎</el-button>
         </div>
       </li>
     </transition-group>
@@ -43,20 +43,23 @@
 </template>
 
 <script setup lang="ts">
-import { listByIds, delOss } from "@/api/system/oss";
 import { propTypes } from '@/utils/propTypes';
-import { globalHeaders } from "@/utils/request";
+import { delOss, listByIds } from '@/api/system/oss';
+import { globalHeaders } from '@/utils/request';
 
 const props = defineProps({
-    modelValue: [String, Object, Array],
-    // 鏁伴噺闄愬埗
-    limit: propTypes.number.def(5),
-    // 澶у皬闄愬埗(MB)
-    fileSize: propTypes.number.def(5),
-    // 鏂囦欢绫诲瀷, 渚嬪['png', 'jpg', 'jpeg']
-    fileType: propTypes.array.def(["doc", "xls", "ppt", "txt", "pdf"]),
-    // 鏄惁鏄剧ず鎻愮ず
-    isShowTip: propTypes.bool.def(true),
+  modelValue: {
+    type: [String, Object, Array],
+    default: () => []
+  },
+  // 鏁伴噺闄愬埗
+  limit: propTypes.number.def(5),
+  // 澶у皬闄愬埗(MB)
+  fileSize: propTypes.number.def(5),
+  // 鏂囦欢绫诲瀷, 渚嬪['png', 'jpg', 'jpeg']
+  fileType: propTypes.array.def(['doc', 'xls', 'ppt', 'txt', 'pdf']),
+  // 鏄惁鏄剧ず鎻愮ず
+  isShowTip: propTypes.bool.def(true)
 });
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@@ -65,153 +68,162 @@
 const uploadList = ref<any[]>([]);
 
 const baseUrl = import.meta.env.VITE_APP_BASE_API;
-const uploadFileUrl = ref(baseUrl + "/resource/oss/upload"); // 涓婁紶鏂囦欢鏈嶅姟鍣ㄥ湴鍧�
+const uploadFileUrl = ref(baseUrl + '/resource/oss/upload'); // 涓婁紶鏂囦欢鏈嶅姟鍣ㄥ湴鍧�
 const headers = ref(globalHeaders());
 
 const fileList = ref<any[]>([]);
-const showTip = computed(
-    () => props.isShowTip && (props.fileType || props.fileSize)
-);
+const showTip = computed(() => props.isShowTip && (props.fileType || props.fileSize));
 
 const fileUploadRef = ref<ElUploadInstance>();
 
-watch(() => props.modelValue, async val => {
+watch(
+  () => props.modelValue,
+  async (val) => {
     if (val) {
-        let temp = 1;
-        // 棣栧厛灏嗗�艰浆涓烘暟缁�
-        let list = [];
-        if (Array.isArray(val)) {
-            list = val;
-        } else {
-            const res = await listByIds(val as string)
-            list = res.data.map((oss) => {
-                const data = { name: oss.originalName, url: oss.url, ossId: oss.ossId };
-                return data;
-            });
-        }
-        // 鐒跺悗灏嗘暟缁勮浆涓哄璞℃暟缁�
-        fileList.value = list.map(item => {
-            item = { name: item.name, url: item.url, ossId: item.ossId };
-            item.uid = item.uid || new Date().getTime() + temp++;
-            return item;
+      let temp = 1;
+      // 棣栧厛灏嗗�艰浆涓烘暟缁�
+      let list: any[] = [];
+      if (Array.isArray(val)) {
+        list = val;
+      } else {
+        const res = await listByIds(val);
+        list = res.data.map((oss) => {
+          return {
+            name: oss.originalName,
+            url: oss.url,
+            ossId: oss.ossId
+          };
         });
+      }
+      // 鐒跺悗灏嗘暟缁勮浆涓哄璞℃暟缁�
+      fileList.value = list.map((item) => {
+        item = { name: item.name, url: item.url, ossId: item.ossId };
+        item.uid = item.uid || new Date().getTime() + temp++;
+        return item;
+      });
     } else {
-        fileList.value = [];
-        return [];
+      fileList.value = [];
+      return [];
     }
-}, { deep: true, immediate: true });
+  },
+  { deep: true, immediate: true }
+);
 
 // 涓婁紶鍓嶆牎妫�鏍煎紡鍜屽ぇ灏�
 const handleBeforeUpload = (file: any) => {
-    // 鏍℃鏂囦欢绫诲瀷
-    if (props.fileType.length) {
-        const fileName = file.name.split('.');
-        const fileExt = fileName[fileName.length - 1];
-        const isTypeOk = props.fileType.indexOf(fileExt) >= 0;
-        if (!isTypeOk) {
-            proxy?.$modal.msgError(`鏂囦欢鏍煎紡涓嶆纭�, 璇蜂笂浼�${props.fileType.join("/")}鏍煎紡鏂囦欢!`);
-            return false;
-        }
+  // 鏍℃鏂囦欢绫诲瀷
+  if (props.fileType.length) {
+    const fileName = file.name.split('.');
+    const fileExt = fileName[fileName.length - 1];
+    const isTypeOk = props.fileType.indexOf(fileExt) >= 0;
+    if (!isTypeOk) {
+      proxy?.$modal.msgError(`鏂囦欢鏍煎紡涓嶆纭�, 璇蜂笂浼�${props.fileType.join('/')}鏍煎紡鏂囦欢!`);
+      return false;
     }
-    // 鏍℃鏂囦欢澶у皬
-    if (props.fileSize) {
-        const isLt = file.size / 1024 / 1024 < props.fileSize;
-        if (!isLt) {
-            proxy?.$modal.msgError(`涓婁紶鏂囦欢澶у皬涓嶈兘瓒呰繃 ${props.fileSize} MB!`);
-            return false;
-        }
+  }
+  // 鏍℃鏂囦欢澶у皬
+  if (props.fileSize) {
+    const isLt = file.size / 1024 / 1024 < props.fileSize;
+    if (!isLt) {
+      proxy?.$modal.msgError(`涓婁紶鏂囦欢澶у皬涓嶈兘瓒呰繃 ${props.fileSize} MB!`);
+      return false;
     }
-    proxy?.$modal.loading("姝e湪涓婁紶鏂囦欢锛岃绋嶅��...");
-    number.value++;
-    return true;
-}
+  }
+  proxy?.$modal.loading('姝e湪涓婁紶鏂囦欢锛岃绋嶅��...');
+  number.value++;
+  return true;
+};
 
 // 鏂囦欢涓暟瓒呭嚭
 const handleExceed = () => {
-    proxy?.$modal.msgError(`涓婁紶鏂囦欢鏁伴噺涓嶈兘瓒呰繃 ${props.limit} 涓�!`);
-}
+  proxy?.$modal.msgError(`涓婁紶鏂囦欢鏁伴噺涓嶈兘瓒呰繃 ${props.limit} 涓�!`);
+};
 
 // 涓婁紶澶辫触
 const handleUploadError = () => {
-    proxy?.$modal.msgError("涓婁紶鏂囦欢澶辫触");
-}
+  proxy?.$modal.msgError('涓婁紶鏂囦欢澶辫触');
+};
 
 // 涓婁紶鎴愬姛鍥炶皟
 const handleUploadSuccess = (res: any, file: UploadFile) => {
-    if (res.code === 200) {
-        uploadList.value.push({ name: res.data.fileName, url: res.data.url, ossId: res.data.ossId });
-        uploadedSuccessfully();
-    } else {
-        number.value--;
-        proxy?.$modal.closeLoading();
-        proxy?.$modal.msgError(res.msg);
-        fileUploadRef.value?.handleRemove(file);
-        uploadedSuccessfully();
-    }
-}
+  if (res.code === 200) {
+    uploadList.value.push({
+      name: res.data.fileName,
+      url: res.data.url,
+      ossId: res.data.ossId
+    });
+    uploadedSuccessfully();
+  } else {
+    number.value--;
+    proxy?.$modal.closeLoading();
+    proxy?.$modal.msgError(res.msg);
+    fileUploadRef.value?.handleRemove(file);
+    uploadedSuccessfully();
+  }
+};
 
 // 鍒犻櫎鏂囦欢
 const handleDelete = (index: number) => {
-    let ossId = fileList.value[index].ossId;
-    delOss(ossId);
-    fileList.value.splice(index, 1);
-    emit("update:modelValue", listToString(fileList.value));
-}
+  let ossId = fileList.value[index].ossId;
+  delOss(ossId);
+  fileList.value.splice(index, 1);
+  emit('update:modelValue', listToString(fileList.value));
+};
 
 // 涓婁紶缁撴潫澶勭悊
 const uploadedSuccessfully = () => {
-    if (number.value > 0 && uploadList.value.length === number.value) {
-        fileList.value = fileList.value.filter(f => f.url !== undefined).concat(uploadList.value);
-        uploadList.value = [];
-        number.value = 0;
-        emit("update:modelValue", listToString(fileList.value));
-        proxy?.$modal.closeLoading();
-    }
-}
+  if (number.value > 0 && uploadList.value.length === number.value) {
+    fileList.value = fileList.value.filter((f) => f.url !== undefined).concat(uploadList.value);
+    uploadList.value = [];
+    number.value = 0;
+    emit('update:modelValue', listToString(fileList.value));
+    proxy?.$modal.closeLoading();
+  }
+};
 
 // 鑾峰彇鏂囦欢鍚嶇О
 const getFileName = (name: string) => {
-    // 濡傛灉鏄痷rl閭d箞鍙栨渶鍚庣殑鍚嶅瓧 濡傛灉涓嶆槸鐩存帴杩斿洖
-    if (name.lastIndexOf("/") > -1) {
-        return name.slice(name.lastIndexOf("/") + 1);
-    } else {
-        return name;
-    }
-}
+  // 濡傛灉鏄痷rl閭d箞鍙栨渶鍚庣殑鍚嶅瓧 濡傛灉涓嶆槸鐩存帴杩斿洖
+  if (name.lastIndexOf('/') > -1) {
+    return name.slice(name.lastIndexOf('/') + 1);
+  } else {
+    return name;
+  }
+};
 
 // 瀵硅薄杞垚鎸囧畾瀛楃涓插垎闅�
 const listToString = (list: any[], separator?: string) => {
-    let strs = "";
-    separator = separator || ",";
-    list.forEach(item => {
-        if (item.ossId) {
-            strs += item.ossId + separator;
-        }
-    })
-    return strs != "" ? strs.substring(0, strs.length - 1) : "";
-}
+  let strs = '';
+  separator = separator || ',';
+  list.forEach((item) => {
+    if (item.ossId) {
+      strs += item.ossId + separator;
+    }
+  });
+  return strs != '' ? strs.substring(0, strs.length - 1) : '';
+};
 </script>
 
 <style scoped lang="scss">
 .upload-file-uploader {
-    margin-bottom: 5px;
+  margin-bottom: 5px;
 }
 
 .upload-file-list .el-upload-list__item {
-    border: 1px solid #e4e7ed;
-    line-height: 2;
-    margin-bottom: 10px;
-    position: relative;
+  border: 1px solid #e4e7ed;
+  line-height: 2;
+  margin-bottom: 10px;
+  position: relative;
 }
 
 .upload-file-list .ele-upload-list__item-content {
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
-    color: inherit;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  color: inherit;
 }
 
 .ele-upload-list__item-content-action .el-link {
-    margin-right: 10px;
+  margin-right: 10px;
 }
 </style>
diff --git a/src/components/Hamburger/index.vue b/src/components/Hamburger/index.vue
index 489ac43..f9f5e62 100644
--- a/src/components/Hamburger/index.vue
+++ b/src/components/Hamburger/index.vue
@@ -1,5 +1,5 @@
 <template>
-  <div style="padding: 0 15px;" @click="toggleClick">
+  <div style="padding: 0 15px" @click="toggleClick">
     <svg :class="{ 'is-active': isActive }" class="hamburger" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="64" height="64">
       <path
         d="M408 442h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8zm-8 204c0 4.4 3.6 8 8 8h480c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8H408c-4.4 0-8 3.6-8 8v56zm504-486H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zm0 632H120c-4.4 0-8 3.6-8 8v56c0 4.4 3.6 8 8 8h784c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8zM142.4 642.1L298.7 519a8.84 8.84 0 0 0 0-13.9L142.4 381.9c-5.8-4.6-14.4-.5-14.4 6.9v246.3a8.9 8.9 0 0 0 14.4 7z"
@@ -13,12 +13,12 @@
 
 defineProps({
   isActive: propTypes.bool.def(false)
-})
+});
 
-const emit = defineEmits(['toggleClick'])
+const emit = defineEmits(['toggleClick']);
 const toggleClick = () => {
   emit('toggleClick');
-}
+};
 </script>
 
 <style scoped>
diff --git a/src/components/HeaderSearch/index.vue b/src/components/HeaderSearch/index.vue
index b922c6c..a785958 100644
--- a/src/components/HeaderSearch/index.vue
+++ b/src/components/HeaderSearch/index.vue
@@ -1,6 +1,6 @@
 <template>
-  <div :class="{ 'show': show }" class="header-search">
-    <svg-icon class-name="search-icon" icon-class="search" @click.stop="click"/>
+  <div :class="{ show: show }" class="header-search">
+    <svg-icon class-name="search-icon" icon-class="search" @click.stop="click" />
     <el-select
       ref="headerSearchSelectRef"
       v-model="search"
@@ -12,23 +12,22 @@
       class="header-search-select"
       @change="change"
     >
-      <el-option v-for="option in options" :key="option.item.path" :value="option.item"
-                 :label="option.item.title.join(' > ')"/>
+      <el-option v-for="option in options" :key="option.item.path" :value="option.item" :label="option.item.title.join(' > ')" />
     </el-select>
   </div>
 </template>
 
 <script setup lang="ts" name="HeaderSearch">
 import Fuse from 'fuse.js';
-import {getNormalPath} from '@/utils/ruoyi';
-import {isHttp} from '@/utils/validate';
+import { getNormalPath } from '@/utils/ruoyi';
+import { isHttp } from '@/utils/validate';
 import usePermissionStore from '@/store/modules/permission';
-import {RouteOption} from 'vue-router';
+import { RouteRecordRaw } from 'vue-router';
 
 type Router = Array<{
   path: string;
   title: string[];
-}>
+}>;
 
 const search = ref('');
 const options = ref<any>([]);
@@ -37,39 +36,39 @@
 const fuse = ref();
 const headerSearchSelectRef = ref<ElSelectInstance>();
 const router = useRouter();
-const routes = computed(() => usePermissionStore().routes);
+const routes = computed(() => usePermissionStore().getRoutes());
 
 const click = () => {
-  show.value = !show.value
+  show.value = !show.value;
   if (show.value) {
-    headerSearchSelectRef.value && headerSearchSelectRef.value.focus()
+    headerSearchSelectRef.value && headerSearchSelectRef.value.focus();
   }
 };
 const close = () => {
-  headerSearchSelectRef.value && headerSearchSelectRef.value.blur()
-  options.value = []
-  show.value = false
-}
+  headerSearchSelectRef.value && headerSearchSelectRef.value.blur();
+  options.value = [];
+  show.value = false;
+};
 const change = (val: any) => {
   const path = val.path;
   const query = val.query;
   if (isHttp(path)) {
     // http(s):// 璺緞鏂扮獥鍙f墦寮�
-    const pindex = path.indexOf("http");
-    window.open(path.substr(pindex, path.length), "_blank");
+    const pindex = path.indexOf('http');
+    window.open(path.substr(pindex, path.length), '_blank');
   } else {
     if (query) {
       router.push({ path: path, query: JSON.parse(query) });
     } else {
-      router.push(path)
+      router.push(path);
     }
   }
-  search.value = ''
-  options.value = []
+  search.value = '';
+  options.value = [];
   nextTick(() => {
-    show.value = false
-  })
-}
+    show.value = false;
+  });
+};
 const initFuse = (list: Router) => {
   fuse.value = new Fuse(list, {
     shouldSort: true,
@@ -77,20 +76,23 @@
     location: 0,
     distance: 100,
     minMatchCharLength: 1,
-    keys: [{
-      name: 'title',
-      weight: 0.7
-    }, {
-      name: 'path',
-      weight: 0.3
-    }]
-  })
-}
+    keys: [
+      {
+        name: 'title',
+        weight: 0.7
+      },
+      {
+        name: 'path',
+        weight: 0.3
+      }
+    ]
+  });
+};
 // Filter out the routes that can be displayed in the sidebar
 // And generate the internationalized title
-const generateRoutes = (routes: RouteOption[], basePath = '', prefixTitle: string[] = []) => {
-  let res: Router = []
-  routes.forEach(r => {
+const generateRoutes = (routes: RouteRecordRaw[], basePath = '', prefixTitle: string[] = []) => {
+  let res: Router = [];
+  routes.forEach((r) => {
     // skip hidden router
     if (!r.hidden) {
       const p = r.path.length > 0 && r.path[0] === '/' ? r.path : '/' + r.path;
@@ -98,7 +100,7 @@
         path: !isHttp(r.path) ? getNormalPath(basePath + p) : r.path,
         title: [...prefixTitle],
         query: ''
-      }
+      };
       if (r.meta && r.meta.title) {
         data.title = [...data.title, r.meta.title];
         if (r.redirect !== 'noRedirect') {
@@ -109,7 +111,7 @@
       }
 
       if (r.query) {
-        data.query = r.query
+        data.query = r.query;
       }
 
       // recursive child routes
@@ -120,20 +122,20 @@
         }
       }
     }
-  })
+  });
   return res;
-}
+};
 const querySearch = (query: string) => {
   if (query !== '') {
-    options.value = fuse.value.search(query)
+    options.value = fuse.value.search(query);
   } else {
-    options.value = []
+    options.value = [];
   }
-}
+};
 
 onMounted(() => {
   searchPool.value = generateRoutes(routes.value);
-})
+});
 
 // watchEffect(() => {
 //     searchPool.value = generateRoutes(routes.value)
@@ -141,15 +143,15 @@
 
 watch(show, (value) => {
   if (value) {
-    document.body.addEventListener('click', close)
+    document.body.addEventListener('click', close);
   } else {
-    document.body.removeEventListener('click', close)
+    document.body.removeEventListener('click', close);
   }
-})
+});
 
-watch(searchPool, (list) => {
-  initFuse(list)
-})
+watch(searchPool, (list: Router) => {
+  initFuse(list);
+});
 </script>
 
 <style lang="scss" scoped>
diff --git a/src/components/IconSelect/index.vue b/src/components/IconSelect/index.vue
index 1cd0023..3bfde4d 100644
--- a/src/components/IconSelect/index.vue
+++ b/src/components/IconSelect/index.vue
@@ -1,6 +1,6 @@
 <template>
-  <div class="relative" :style="{ width: width }">
-    <el-input v-model="modelValue" readonly @click="visible = !visible" placeholder="鐐瑰嚮閫夋嫨鍥炬爣">
+  <div class="relative" :style="{ 'width': width }">
+    <el-input v-model="modelValue" readonly placeholder="鐐瑰嚮閫夋嫨鍥炬爣" @click="visible = !visible">
       <template #prepend>
         <svg-icon :icon-class="modelValue" />
       </template>
@@ -8,18 +8,18 @@
 
     <el-popover shadow="none" :visible="visible" placement="bottom-end" trigger="click" :width="450">
       <template #reference>
-        <div @click="visible = !visible" class="cursor-pointer text-[#999] absolute right-[10px] top-0 height-[32px] leading-[32px]">
+        <div class="cursor-pointer text-[#999] absolute right-[10px] top-0 height-[32px] leading-[32px]" @click="visible = !visible">
           <i-ep-caret-top v-show="visible"></i-ep-caret-top>
           <i-ep-caret-bottom v-show="!visible"></i-ep-caret-bottom>
         </div>
       </template>
 
-      <el-input class="p-2" v-model="filterValue" placeholder="鎼滅储鍥炬爣" clearable @input="filterIcons" />
+      <el-input v-model="filterValue" class="p-2" placeholder="鎼滅储鍥炬爣" clearable @input="filterIcons" />
 
       <el-scrollbar height="w-[200px]">
         <ul class="icon-list">
           <el-tooltip v-for="(iconName, index) in iconNames" :key="index" :content="iconName" placement="bottom" effect="light">
-            <li :class="['icon-item', {active: modelValue == iconName}]" @click="selectedIcon(iconName)">
+            <li :class="['icon-item', { active: modelValue == iconName }]" @click="selectedIcon(iconName)">
               <svg-icon color="var(--el-text-color-regular)" :icon-class="iconName" />
             </li>
           </el-tooltip>
@@ -50,13 +50,11 @@
  */
 const filterIcons = () => {
   if (filterValue.value) {
-    iconNames.value = icons.filter(iconName =>
-      iconName.includes(filterValue.value)
-    );
+    iconNames.value = icons.filter((iconName) => iconName.includes(filterValue.value));
   } else {
     iconNames.value = icons;
   }
-}
+};
 /**
  * 閫夋嫨鍥炬爣
  * @param iconName 閫夋嫨鐨勫浘鏍囧悕绉�
@@ -64,12 +62,12 @@
 const selectedIcon = (iconName: string) => {
   emit('update:modelValue', iconName);
   visible.value = false;
-}
+};
 </script>
 
 <style scoped lang="scss">
 .el-scrollbar {
-  max-height: calc(50vh - 100px)!important;
+  max-height: calc(50vh - 100px) !important;
   overflow-y: auto;
 }
 .el-divider--horizontal {
@@ -99,8 +97,8 @@
     }
   }
   .active {
-      border-color: var(--el-color-primary);
-      color: var(--el-color-primary);
-    }
+    border-color: var(--el-color-primary);
+    color: var(--el-color-primary);
+  }
 }
 </style>
diff --git a/src/components/ImagePreview/index.vue b/src/components/ImagePreview/index.vue
index 723ccdb..349f729 100644
--- a/src/components/ImagePreview/index.vue
+++ b/src/components/ImagePreview/index.vue
@@ -15,11 +15,11 @@
   src: propTypes.string.def(''),
   width: {
     type: [Number, String],
-    default: ""
+    default: ''
   },
   height: {
     type: [Number, String],
-    default: ""
+    default: ''
   }
 });
 
@@ -27,29 +27,28 @@
   if (!props.src) {
     return;
   }
-  let real_src = props.src.split(",")[0];
+  let real_src = props.src.split(',')[0];
   return real_src;
 });
 
 const realSrcList = computed(() => {
   if (!props.src) {
-    return;
+    return [];
   }
-  let real_src_list = props.src.split(",");
+  let real_src_list = props.src.split(',');
   let srcList: string[] = [];
-  real_src_list.forEach(item => {
+  real_src_list.forEach((item: string) => {
+    if(item.trim() === '') {
+      return;
+    }
     return srcList.push(item);
   });
   return srcList;
 });
 
-const realWidth = computed(() =>
-  typeof props.width == "string" ? props.width : `${props.width}px`
-);
+const realWidth = computed(() => (typeof props.width == 'string' ? props.width : `${props.width}px`));
 
-const realHeight = computed(() =>
-  typeof props.height == "string" ? props.height : `${props.height}px`
-);
+const realHeight = computed(() => (typeof props.height == 'string' ? props.height : `${props.height}px`));
 </script>
 
 <style lang="scss" scoped>
diff --git a/src/components/ImageUpload/index.vue b/src/components/ImageUpload/index.vue
index d8ed32b..1c6dcd1 100644
--- a/src/components/ImageUpload/index.vue
+++ b/src/components/ImageUpload/index.vue
@@ -1,6 +1,7 @@
 <template>
   <div class="component-upload-image">
     <el-upload
+      ref="imageUpload"
       multiple
       :action="uploadImgUrl"
       list-type="picture-card"
@@ -9,7 +10,6 @@
       :limit="limit"
       :on-error="handleUploadError"
       :on-exceed="handleExceed"
-      ref="imageUpload"
       :before-remove="handleDelete"
       :show-file-list="true"
       :headers="headers"
@@ -22,13 +22,13 @@
       </el-icon>
     </el-upload>
     <!-- 涓婁紶鎻愮ず -->
-    <div class="el-upload__tip" v-if="showTip">
+    <div v-if="showTip" class="el-upload__tip">
       璇蜂笂浼�
       <template v-if="fileSize">
         澶у皬涓嶈秴杩� <b style="color: #f56c6c">{{ fileSize }}MB</b>
       </template>
       <template v-if="fileType">
-        鏍煎紡涓� <b style="color: #f56c6c">{{ fileType.join("/") }}</b>
+        鏍煎紡涓� <b style="color: #f56c6c">{{ fileType.join('/') }}</b>
       </template>
       鐨勬枃浠�
     </div>
@@ -40,177 +40,195 @@
 </template>
 
 <script setup lang="ts">
-import { listByIds, delOss } from "@/api/system/oss";
-import { ComponentInternalInstance } from "vue";
-import { OssVO } from "@/api/system/oss/types";
+import { listByIds, delOss } from '@/api/system/oss';
+import { OssVO } from '@/api/system/oss/types';
 import { propTypes } from '@/utils/propTypes';
-import {globalHeaders} from "@/utils/request";
+import { globalHeaders } from '@/utils/request';
+import { compressAccurately } from 'image-conversion';
 
 const props = defineProps({
-    modelValue: [String, Object, Array],
-    // 鍥剧墖鏁伴噺闄愬埗
-    limit: propTypes.number.def(5),
-    // 澶у皬闄愬埗(MB)
-    fileSize: propTypes.number.def(5),
-    // 鏂囦欢绫诲瀷, 渚嬪['png', 'jpg', 'jpeg']
-    fileType: propTypes.array.def(["png", "jpg", "jpeg"]),
-    // 鏄惁鏄剧ず鎻愮ず
-    isShowTip: {
-        type: Boolean,
-        default: true
-    },
+  modelValue: {
+    type: [String, Object, Array],
+    default: () => []
+  },
+  // 鍥剧墖鏁伴噺闄愬埗
+  limit: propTypes.number.def(5),
+  // 澶у皬闄愬埗(MB)
+  fileSize: propTypes.number.def(5),
+  // 鏂囦欢绫诲瀷, 渚嬪['png', 'jpg', 'jpeg']
+  fileType: propTypes.array.def(['png', 'jpg', 'jpeg']),
+  // 鏄惁鏄剧ず鎻愮ず
+  isShowTip: {
+    type: Boolean,
+    default: true
+  },
+  // 鏄惁鏀寔鍘嬬缉锛岄粯璁ゅ惁
+  compressSupport: {
+    type: Boolean,
+    default: false
+  },
+  // 鍘嬬缉鐩爣澶у皬锛屽崟浣岾B銆傞粯璁�300KB浠ヤ笂鏂囦欢鎵嶅帇缂╋紝骞跺帇缂╄嚦300KB浠ュ唴
+  compressTargetSize: propTypes.number.def(300)
 });
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 const emit = defineEmits(['update:modelValue']);
 const number = ref(0);
 const uploadList = ref<any[]>([]);
-const dialogImageUrl = ref("");
+const dialogImageUrl = ref('');
 const dialogVisible = ref(false);
 
 const baseUrl = import.meta.env.VITE_APP_BASE_API;
-const uploadImgUrl = ref(baseUrl + "/resource/oss/upload"); // 涓婁紶鐨勫浘鐗囨湇鍔″櫒鍦板潃
+const uploadImgUrl = ref(baseUrl + '/resource/oss/upload'); // 涓婁紶鐨勫浘鐗囨湇鍔″櫒鍦板潃
 const headers = ref(globalHeaders());
 
 const fileList = ref<any[]>([]);
-const showTip = computed(
-    () => props.isShowTip && (props.fileType || props.fileSize)
-);
+const showTip = computed(() => props.isShowTip && (props.fileType || props.fileSize));
 
 const imageUploadRef = ref<ElUploadInstance>();
 
-watch(() => props.modelValue, async val => {
+watch(
+  () => props.modelValue,
+  async (val: string) => {
     if (val) {
-        // 棣栧厛灏嗗�艰浆涓烘暟缁�
-        let list: OssVO[] = [];
-        if (Array.isArray(val)) {
-            list = val as OssVO[];
+      // 棣栧厛灏嗗�艰浆涓烘暟缁�
+      let list: OssVO[] = [];
+      if (Array.isArray(val)) {
+        list = val as OssVO[];
+      } else {
+        const res = await listByIds(val);
+        list = res.data;
+      }
+      // 鐒跺悗灏嗘暟缁勮浆涓哄璞℃暟缁�
+      fileList.value = list.map((item) => {
+        // 瀛楃涓插洖鏄惧鐞� 濡傛灉姝ゅ瀛樼殑鏄痷rl鍙洿鎺ュ洖鏄� 濡傛灉瀛樼殑鏄痠d闇�瑕佽皟鐢ㄦ帴鍙f煡鍑烘潵
+        let itemData;
+        if (typeof item === 'string') {
+          itemData = { name: item, url: item };
         } else {
-            const res = await listByIds(val as string)
-            list = res.data
+          // 姝ゅname浣跨敤ossId 闃叉鍒犻櫎鍑虹幇閲嶅悕
+          itemData = { name: item.ossId, url: item.url, ossId: item.ossId };
         }
-        // 鐒跺悗灏嗘暟缁勮浆涓哄璞℃暟缁�
-        fileList.value = list.map(item => {
-            // 瀛楃涓插洖鏄惧鐞� 濡傛灉姝ゅ瀛樼殑鏄痷rl鍙洿鎺ュ洖鏄� 濡傛灉瀛樼殑鏄痠d闇�瑕佽皟鐢ㄦ帴鍙f煡鍑烘潵
-            let itemData;
-            if (typeof item === "string") {
-                itemData = { name: item, url: item };
-            } else {
-                // 姝ゅname浣跨敤ossId 闃叉鍒犻櫎鍑虹幇閲嶅悕
-                itemData = { name: item.ossId, url: item.url, ossId: item.ossId };
-            }
-            return itemData;
-        });
+        return itemData;
+      });
     } else {
-        fileList.value = [];
-        return [];
+      fileList.value = [];
+      return [];
     }
-}, { deep: true, immediate: true });
+  },
+  { deep: true, immediate: true }
+);
 
 /** 涓婁紶鍓峫oading鍔犺浇 */
 const handleBeforeUpload = (file: any) => {
-    let isImg = false;
-    if (props.fileType.length) {
-        let fileExtension = "";
-        if (file.name.lastIndexOf(".") > -1) {
-            fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1);
-        }
-        isImg = props.fileType.some((type: any) => {
-            if (file.type.indexOf(type) > -1) return true;
-            if (fileExtension && fileExtension.indexOf(type) > -1) return true;
-            return false;
-        });
-    } else {
-        isImg = file.type.indexOf("image") > -1;
+  let isImg = false;
+  if (props.fileType.length) {
+    let fileExtension = '';
+    if (file.name.lastIndexOf('.') > -1) {
+      fileExtension = file.name.slice(file.name.lastIndexOf('.') + 1);
     }
-    if (!isImg) {
-        proxy?.$modal.msgError(
-            `鏂囦欢鏍煎紡涓嶆纭�, 璇蜂笂浼�${props.fileType.join("/")}鍥剧墖鏍煎紡鏂囦欢!`
-        );
-        return false;
+    isImg = props.fileType.some((type: any) => {
+      if (file.type.indexOf(type) > -1) return true;
+      if (fileExtension && fileExtension.indexOf(type) > -1) return true;
+      return false;
+    });
+  } else {
+    isImg = file.type.indexOf('image') > -1;
+  }
+  if (!isImg) {
+    proxy?.$modal.msgError(`鏂囦欢鏍煎紡涓嶆纭�, 璇蜂笂浼�${props.fileType.join('/')}鍥剧墖鏍煎紡鏂囦欢!`);
+    return false;
+  }
+  if (props.fileSize) {
+    const isLt = file.size / 1024 / 1024 < props.fileSize;
+    if (!isLt) {
+      proxy?.$modal.msgError(`涓婁紶澶村儚鍥剧墖澶у皬涓嶈兘瓒呰繃 ${props.fileSize} MB!`);
+      return false;
     }
-    if (props.fileSize) {
-        const isLt = file.size / 1024 / 1024 < props.fileSize;
-        if (!isLt) {
-            proxy?.$modal.msgError(`涓婁紶澶村儚鍥剧墖澶у皬涓嶈兘瓒呰繃 ${props.fileSize} MB!`);
-            return false;
-        }
-    }
-    proxy?.$modal.loading("姝e湪涓婁紶鍥剧墖锛岃绋嶅��...");
+  }
+
+  //鍘嬬缉鍥剧墖锛屽紑鍚帇缂╁苟涓斿ぇ浜庢寚瀹氱殑鍘嬬缉澶у皬鏃舵墠鍘嬬缉
+  if (props.compressSupport && file.size / 1024 > props.compressTargetSize) {
+    proxy?.$modal.loading('姝e湪涓婁紶鍥剧墖锛岃绋嶅��...');
     number.value++;
-}
+    return compressAccurately(file, props.compressTargetSize);
+  } else {
+    proxy?.$modal.loading('姝e湪涓婁紶鍥剧墖锛岃绋嶅��...');
+    number.value++;
+  }
+};
 
 // 鏂囦欢涓暟瓒呭嚭
 const handleExceed = () => {
-    proxy?.$modal.msgError(`涓婁紶鏂囦欢鏁伴噺涓嶈兘瓒呰繃 ${props.limit} 涓�!`);
-}
+  proxy?.$modal.msgError(`涓婁紶鏂囦欢鏁伴噺涓嶈兘瓒呰繃 ${props.limit} 涓�!`);
+};
 
 // 涓婁紶鎴愬姛鍥炶皟
 const handleUploadSuccess = (res: any, file: UploadFile) => {
-    if (res.code === 200) {
-        uploadList.value.push({ name: res.data.fileName, url: res.data.url, ossId: res.data.ossId });
-        uploadedSuccessfully();
-    } else {
-        number.value--;
-        proxy?.$modal.closeLoading();
-        proxy?.$modal.msgError(res.msg);
-        imageUploadRef.value?.handleRemove(file);
-        uploadedSuccessfully();
-    }
-}
+  if (res.code === 200) {
+    uploadList.value.push({ name: res.data.fileName, url: res.data.url, ossId: res.data.ossId });
+    uploadedSuccessfully();
+  } else {
+    number.value--;
+    proxy?.$modal.closeLoading();
+    proxy?.$modal.msgError(res.msg);
+    imageUploadRef.value?.handleRemove(file);
+    uploadedSuccessfully();
+  }
+};
 
 // 鍒犻櫎鍥剧墖
 const handleDelete = (file: UploadFile): boolean => {
-    const findex = fileList.value.map(f => f.name).indexOf(file.name);
-    if (findex > -1 && uploadList.value.length === number.value) {
-        let ossId = fileList.value[findex].ossId;
-        delOss(ossId);
-        fileList.value.splice(findex, 1);
-        emit("update:modelValue", listToString(fileList.value));
-        return false;
-    }
-    return true;
-}
+  const findex = fileList.value.map((f) => f.name).indexOf(file.name);
+  if (findex > -1 && uploadList.value.length === number.value) {
+    let ossId = fileList.value[findex].ossId;
+    delOss(ossId);
+    fileList.value.splice(findex, 1);
+    emit('update:modelValue', listToString(fileList.value));
+    return false;
+  }
+  return true;
+};
 
 // 涓婁紶缁撴潫澶勭悊
 const uploadedSuccessfully = () => {
-    if (number.value > 0 && uploadList.value.length === number.value) {
-        fileList.value = fileList.value.filter(f => f.url !== undefined).concat(uploadList.value);
-        uploadList.value = [];
-        number.value = 0;
-        emit("update:modelValue", listToString(fileList.value));
-        proxy?.$modal.closeLoading();
-    }
-}
+  if (number.value > 0 && uploadList.value.length === number.value) {
+    fileList.value = fileList.value.filter((f) => f.url !== undefined).concat(uploadList.value);
+    uploadList.value = [];
+    number.value = 0;
+    emit('update:modelValue', listToString(fileList.value));
+    proxy?.$modal.closeLoading();
+  }
+};
 
 // 涓婁紶澶辫触
 const handleUploadError = () => {
-    proxy?.$modal.msgError("涓婁紶鍥剧墖澶辫触");
-    proxy?.$modal.closeLoading();
-}
+  proxy?.$modal.msgError('涓婁紶鍥剧墖澶辫触');
+  proxy?.$modal.closeLoading();
+};
 
 // 棰勮
 const handlePictureCardPreview = (file: any) => {
-    dialogImageUrl.value = file.url;
-    dialogVisible.value = true;
-}
+  dialogImageUrl.value = file.url;
+  dialogVisible.value = true;
+};
 
 // 瀵硅薄杞垚鎸囧畾瀛楃涓插垎闅�
 const listToString = (list: any[], separator?: string) => {
-    let strs = "";
-    separator = separator || ",";
-    for (let i in list) {
-        if (undefined !== list[i].ossId && list[i].url.indexOf("blob:") !== 0) {
-            strs += list[i].ossId + separator;
-        }
+  let strs = '';
+  separator = separator || ',';
+  for (let i in list) {
+    if (undefined !== list[i].ossId && list[i].url.indexOf('blob:') !== 0) {
+      strs += list[i].ossId + separator;
     }
-    return strs != "" ? strs.substring(0, strs.length - 1) : "";
-}
+  }
+  return strs != '' ? strs.substring(0, strs.length - 1) : '';
+};
 </script>
 
 <style scoped lang="scss">
 // .el-upload--picture-card 鎺у埗鍔犲彿閮ㄥ垎
 :deep(.hide .el-upload--picture-card) {
-    display: none;
+  display: none;
 }
 </style>
diff --git a/src/components/LangSelect/index.vue b/src/components/LangSelect/index.vue
index a30a2f6..b5fafd3 100644
--- a/src/components/LangSelect/index.vue
+++ b/src/components/LangSelect/index.vue
@@ -14,22 +14,21 @@
 
 <script setup lang="ts">
 import { useI18n } from 'vue-i18n';
-import SvgIcon from '@/components/SvgIcon/index.vue';
 import { useAppStore } from '@/store/modules/app';
+import SvgIcon from '@/components/SvgIcon/index.vue';
 
 const appStore = useAppStore();
 const { locale } = useI18n();
 
-
 const message: any = {
   zh_CN: '鍒囨崲璇█鎴愬姛锛�',
-  en_US: 'Switch Language Successful!',
-}
-const handleLanguageChange = (lang: string) => {
+  en_US: 'Switch Language Successful!'
+};
+const handleLanguageChange = (lang: any) => {
   locale.value = lang;
   appStore.changeLanguage(lang);
   ElMessage.success(message[lang] || '鍒囨崲璇█鎴愬姛锛�');
-}
+};
 </script>
 
 <style lang="scss" scoped>
diff --git a/src/components/Pagination/index.vue b/src/components/Pagination/index.vue
index df73ceb..ac02193 100644
--- a/src/components/Pagination/index.vue
+++ b/src/components/Pagination/index.vue
@@ -1,9 +1,9 @@
 <template>
-  <div :class="{ 'hidden': hidden }" class="pagination-container">
+  <div :class="{ hidden: hidden }" class="pagination-container">
     <el-pagination
-      :background="background"
       v-model:current-page="currentPage"
       v-model:page-size="pageSize"
+      :background="background"
       :layout="layout"
       :page-sizes="pageSizes"
       :pager-count="pagerCount"
@@ -16,69 +16,69 @@
 
 <script lang="ts">
 export default {
-    name: 'Pagination'
-}
+  name: 'Pagination'
+};
 </script>
 
 <script setup lang="ts">
-import { scrollTo } from '@/utils/scroll-to'
-import { propTypes } from "@/utils/propTypes";
+import { scrollTo } from '@/utils/scroll-to';
+import { propTypes } from '@/utils/propTypes';
 
 const props = defineProps({
-    total: propTypes.number,
-    page: propTypes.number.def(1),
-    limit: propTypes.number.def(20),
-    pageSizes: {
-      type: Array as PropType<number[]>,
-      default: () => [10, 20, 30, 50]
-    },
-    // 绉诲姩绔〉鐮佹寜閽殑鏁伴噺绔粯璁ゅ��5
-    pagerCount: propTypes.number.def(document.body.clientWidth < 992 ? 5 : 7),
-    layout: propTypes.string.def('total, sizes, prev, pager, next, jumper'),
-    background: propTypes.bool.def(true),
-    autoScroll: propTypes.bool.def(true),
-    hidden: propTypes.bool.def(false),
-    float: propTypes.string.def('right')
-})
+  total: propTypes.number,
+  page: propTypes.number.def(1),
+  limit: propTypes.number.def(20),
+  pageSizes: {
+    type: Array,
+    default: () => [10, 20, 30, 50]
+  },
+  // 绉诲姩绔〉鐮佹寜閽殑鏁伴噺绔粯璁ゅ��5
+  pagerCount: propTypes.number.def(document.body.clientWidth < 992 ? 5 : 7),
+  layout: propTypes.string.def('total, sizes, prev, pager, next, jumper'),
+  background: propTypes.bool.def(true),
+  autoScroll: propTypes.bool.def(true),
+  hidden: propTypes.bool.def(false),
+  float: propTypes.string.def('right')
+});
 
 const emit = defineEmits(['update:page', 'update:limit', 'pagination']);
 const currentPage = computed({
-    get() {
-        return props.page
-    },
-    set(val) {
-        emit('update:page', val)
-    }
-})
+  get() {
+    return props.page;
+  },
+  set(val) {
+    emit('update:page', val);
+  }
+});
 const pageSize = computed({
-    get() {
-        return props.limit
-    },
-    set(val){
-        emit('update:limit', val)
-    }
-})
+  get() {
+    return props.limit;
+  },
+  set(val) {
+    emit('update:limit', val);
+  }
+});
 function handleSizeChange(val: number) {
-    if (currentPage.value * val > props.total) {
-        currentPage.value = 1
-    }
-    emit('pagination', { page: currentPage.value, limit: val })
-    if (props.autoScroll) {
-        scrollTo(0, 800)
-    }
+  if (currentPage.value * val > props.total) {
+    currentPage.value = 1;
+  }
+  emit('pagination', { page: currentPage.value, limit: val });
+  if (props.autoScroll) {
+    scrollTo(0, 800);
+  }
 }
 function handleCurrentChange(val: number) {
-    emit('pagination', { page: val, limit: pageSize.value })
-    if (props.autoScroll) {
-        scrollTo(0, 800)
-    }
+  emit('pagination', { page: val, limit: pageSize.value });
+  if (props.autoScroll) {
+    scrollTo(0, 800);
+  }
 }
 </script>
 
 <style lang="scss" scoped>
 .pagination-container {
   padding: 32px 16px;
-  .el-pagination{
+  .el-pagination {
     float: v-bind(float);
   }
 }
diff --git a/src/components/Process/approvalRecord.vue b/src/components/Process/approvalRecord.vue
new file mode 100644
index 0000000..cbce87a
--- /dev/null
+++ b/src/components/Process/approvalRecord.vue
@@ -0,0 +1,116 @@
+<template>
+  <div class="container">
+    <el-dialog v-model="visible" draggable title="瀹℃壒璁板綍" :width="props.width" :height="props.height" :close-on-click-modal="false">
+      <el-tabs v-model="tabActiveName" class="demo-tabs">
+        <el-tab-pane label="娴佺▼鍥�" name="bpmn">
+          <BpmnView ref="bpmnViewRef"></BpmnView>
+        </el-tab-pane>
+        <el-tab-pane v-loading="loading" label="瀹℃壒淇℃伅" name="info">
+          <div>
+            <el-table :data="historyList" style="width: 100%" border fit>
+              <el-table-column type="index" label="搴忓彿" align="center" width="60"></el-table-column>
+              <el-table-column prop="name" label="浠诲姟鍚嶇О" sortable align="center"></el-table-column>
+              <el-table-column prop="nickName" :show-overflow-tooltip="true" label="鍔炵悊浜�" sortable align="center">
+                <template #default="scope">
+                  <el-tag type="success">{{ scope.row.nickName || '鏃�' }}</el-tag>
+                </template>
+              </el-table-column>
+              <el-table-column label="鐘舵��" sortable align="center">
+                <template #default="scope">
+                  <el-tag type="success">{{ scope.row.statusName }}</el-tag>
+                </template>
+              </el-table-column>
+              <el-table-column prop="comment" label="瀹℃壒鎰忚" sortable align="center"></el-table-column>
+              <el-table-column prop="startTime" label="寮�濮嬫椂闂�" sortable align="center"></el-table-column>
+              <el-table-column prop="endTime" label="缁撴潫鏃堕棿" sortable align="center"></el-table-column>
+              <el-table-column prop="runDuration" label="杩愯鏃堕暱" sortable align="center"></el-table-column>
+              <el-table-column prop="attachmentList" label="闄勪欢" sortable align="center">
+                <template #default="scope">
+                  <el-popover v-if="scope.row.attachmentList && scope.row.attachmentList.length > 0" placement="right" :width="310" trigger="click">
+                    <template #reference>
+                      <el-button style="margin-right: 16px">闄勪欢</el-button>
+                    </template>
+                    <el-table border :data="scope.row.attachmentList">
+                      <el-table-column prop="name" width="202" :show-overflow-tooltip="true" label="闄勪欢鍚嶇О"></el-table-column>
+                      <el-table-column prop="name" width="80" align="center" :show-overflow-tooltip="true" label="鎿嶄綔">
+                        <template #default="tool">
+                          <el-button type="text" @click="handleDownload(tool.row.contentId)">涓嬭浇</el-button>
+                        </template>
+                      </el-table-column>
+                    </el-table>
+                  </el-popover>
+                </template>
+              </el-table-column>
+            </el-table>
+          </div>
+        </el-tab-pane>
+      </el-tabs>
+    </el-dialog>
+  </div>
+</template>
+<script lang="ts" setup>
+import BpmnView from '@/components/BpmnView/index.vue';
+import processApi from '@/api/workflow/processInstance';
+import { propTypes } from '@/utils/propTypes';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const props = defineProps({
+  width: propTypes.string.def('70%'),
+  height: propTypes.string.def('100%')
+});
+const loading = ref(false);
+const visible = ref(false);
+const historyList = ref<Array<any>>([]);
+const tabActiveName = ref('bpmn');
+
+const bpmnViewRef = ref<BpmnView>();
+
+//鍒濆鍖栨煡璇㈠鎵硅褰�
+const init = async (instanceId: string) => {
+  visible.value = true;
+  loading.value = true;
+  tabActiveName.value = 'bpmn';
+  historyList.value = [];
+  processApi.getHistoryRecord(instanceId).then((resp) => {
+    historyList.value = resp.data;
+    loading.value = false;
+  });
+  await nextTick(() => {
+    bpmnViewRef.value.init(instanceId);
+  });
+};
+
+/** 涓嬭浇鎸夐挳鎿嶄綔 */
+const handleDownload = (ossId: string) => {
+  proxy?.$download.oss(ossId);
+};
+/**
+ * 瀵瑰鏆撮湶瀛愮粍浠舵柟娉�
+ */
+defineExpose({
+  init
+});
+</script>
+<style lang="scss" scoped>
+.triangle {
+  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
+  border-radius: 6px;
+}
+
+.triangle::after {
+  content: ' ';
+  position: absolute;
+  top: 8em;
+  right: 215px;
+  border: 15px solid;
+  border-color: transparent #fff transparent transparent;
+}
+
+.container {
+  :deep(.el-dialog .el-dialog__body) {
+    max-height: calc(100vh - 170px) !important;
+    min-height: calc(100vh - 170px) !important;
+  }
+}
+</style>
diff --git a/src/components/Process/multiInstanceUser.vue b/src/components/Process/multiInstanceUser.vue
new file mode 100644
index 0000000..b2039b9
--- /dev/null
+++ b/src/components/Process/multiInstanceUser.vue
@@ -0,0 +1,368 @@
+<template>
+  <el-dialog v-model="visible" draggable :title="title" :width="width" :height="height" append-to-body
+    :close-on-click-modal="false">
+    <div class="p-2" v-if="multiInstance === 'add'">
+      <el-row :gutter="20">
+        <!-- 閮ㄩ棬鏍� -->
+        <el-col :lg="4" :xs="24" style="">
+          <el-card shadow="hover">
+            <el-input v-model="deptName" placeholder="璇疯緭鍏ラ儴闂ㄥ悕绉�" prefix-icon="Search" clearable />
+            <el-tree class="mt-2" ref="deptTreeRef" node-key="id" :data="deptOptions"
+              :props="{ label: 'label', children: 'children' }" :expand-on-click-node="false"
+              :filter-node-method="filterNode" highlight-current default-expand-all
+              @node-click="handleNodeClick"></el-tree>
+          </el-card>
+        </el-col>
+        <el-col :lg="20" :xs="24">
+          <transition :enter-active-class="proxy?.animate.searchAnimate.enter"
+            :leave-active-class="proxy?.animate.searchAnimate.leave">
+            <div class="search" v-show="showSearch">
+              <el-form ref="queryFormRef" :model="queryParams" :inline="true">
+                <el-form-item label="鐢ㄦ埛鍚嶇О" prop="userName">
+                  <el-input v-model="queryParams.userName" placeholder="璇疯緭鍏ョ敤鎴峰悕绉�" clearable @keyup.enter="handleQuery" />
+                </el-form-item>
+                <el-form-item label="鎵嬫満鍙风爜" prop="phonenumber">
+                  <el-input v-model="queryParams.phonenumber" placeholder="璇疯緭鍏ユ墜鏈哄彿鐮�" clearable @keyup.enter="handleQuery" />
+                </el-form-item>
+                <el-form-item>
+                  <el-button type="primary" @click="handleQuery" icon="Search">鎼滅储</el-button>
+                  <el-button @click="resetQuery" icon="Refresh">閲嶇疆</el-button>
+                </el-form-item>
+              </el-form>
+            </div>
+          </transition>
+
+          <el-card shadow="hover">
+            <template #header>
+              <el-row :gutter="10">
+                <right-toolbar v-model:showSearch="showSearch" @queryTable="handleQuery" :search="true"></right-toolbar>
+              </el-row>
+            </template>
+
+            <el-table v-loading="loading" :data="userList" ref="multipleTableRef" row-key="userId"
+              @selection-change="handleSelectionChange">
+              <el-table-column type="selection" width="50" align="center" />
+              <el-table-column label="鐢ㄦ埛缂栧彿" align="center" key="userId" prop="userId" />
+              <el-table-column label="鐢ㄦ埛鍚嶇О" align="center" key="userName" prop="userName" :show-overflow-tooltip="true" />
+              <el-table-column label="鐢ㄦ埛鏄电О" align="center" key="nickName" prop="nickName" :show-overflow-tooltip="true" />
+              <el-table-column label="鎵嬫満鍙风爜" align="center" key="phonenumber" prop="phonenumber" width="120" />
+              <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="160">
+                <template #default="scope">
+                  <span>{{ scope.row.createTime }}</span>
+                </template>
+              </el-table-column>
+            </el-table>
+
+            <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
+              v-model:limit="queryParams.pageSize" @pagination="handleQuery" />
+          </el-card>
+          <el-card shadow="hover">
+            <el-tag v-for="(user, index) in chooseUserList" :key="user.userId" style="margin:2px" closable
+              @close="handleCloseTag(user, index)">{{ user.userName }}
+            </el-tag>
+          </el-card>
+        </el-col>
+      </el-row>
+    </div>
+    <div class="p-2" v-if="multiInstance === 'delete'">
+      <el-table v-loading="loading" :data="taskList" @selection-change="handleTaskSelection">
+        <el-table-column type="selection" width="55" />
+        <el-table-column prop="name" label="浠诲姟鍚嶇О" />
+        <el-table-column prop="assigneeName" label="鍔炵悊浜�" />
+      </el-table>
+    </div>
+    <template #footer>
+      <div class="dialog-footer">
+        <el-button type="primary" @click="submitFileForm">纭� 瀹�</el-button>
+        <el-button @click="visible = false">鍙� 娑�</el-button>
+      </div>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup name="User" lang="ts">
+import { deptTreeSelect, listUser, optionSelect } from '@/api/system/user';
+import {
+  addMultiInstanceExecution,
+  deleteMultiInstanceExecution,
+  getTaskUserIdsByAddMultiInstance,
+  getListByDeleteMultiInstance
+} from '@/api/workflow/task';
+import { UserVO } from '@/api/system/user/types';
+import { DeptVO } from '@/api/system/dept/types';
+import { ComponentInternalInstance } from 'vue';
+import { ElTree, ElTable } from 'element-plus';
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const props = defineProps({
+  // 瀹�
+  width: {
+    type: String,
+    default: '70%'
+  },
+  // 楂�
+  height: {
+    type: String,
+    default: '100%'
+  },
+  // 鏍囬
+  title: {
+    type: String,
+    default: '鍔犵浜哄憳'
+  },
+  //鏄惁澶氶��
+  multiple: {
+    type: Boolean,
+    default: true
+  },
+  //鍥炴樉鐢ㄦ埛id
+  userIdList: {
+    type: Array,
+    default: []
+  }
+});
+const deptTreeRef = ref(ElTree);
+const multipleTableRef = ref(ElTable);
+
+const userList = ref<UserVO[]>();
+const taskList = ref<Array<any>[]>();
+const loading = ref(true);
+const showSearch = ref(true);
+const selectionTask = ref<Array<any>[]>();
+const visible = ref(false);
+const total = ref(0);
+const deptName = ref('');
+const deptOptions = ref<DeptVO[]>([]);
+const chooseUserList = ref(ref<UserVO[]>());
+const userIds = ref<Array<number | string>>([]);
+//鍔犵鎴栬�呭噺绛�
+const multiInstance = ref('');
+const queryParams = ref<Record<string, any>>({
+  pageNum: 1,
+  pageSize: 10,
+  userName: '',
+  nickName: '',
+  taskId: ''
+});
+/** 鏌ヨ鐢ㄦ埛鍒楄〃 */
+const getAddMultiInstanceList = async (taskId: string, userIdList: Array<number | string>) => {
+  deptOptions.value = [];
+  getTreeSelect();
+  multiInstance.value = 'add';
+  userIds.value = userIdList;
+  visible.value = true;
+  queryParams.value.taskId = taskId;
+  loading.value = true;
+  const res1 = await getTaskUserIdsByAddMultiInstance(taskId);
+  queryParams.value.excludeUserIds = res1.data;
+  const res = await listUser(queryParams.value);
+  loading.value = false;
+  userList.value = res.rows;
+  total.value = res.total;
+  if (userList.value && userIds.value.length > 0) {
+    const data = await optionSelect(userIds.value);
+    if (data.data && data.data.length > 0) {
+      chooseUserList.value = data.data;
+      data.data.forEach((user: UserVO) => {
+        multipleTableRef.value!.toggleRowSelection(
+          userList.value.find((item) => {
+            return item.userId == user.userId;
+          }),
+          true
+        );
+      });
+    }
+  }
+};
+
+const getList = async () => {
+  loading.value = true;
+  const res1 = await getTaskUserIdsByAddMultiInstance(queryParams.value.taskId);
+  queryParams.value.excludeUserIds = res1.data;
+  const res = await listUser(queryParams.value);
+  loading.value = false;
+  userList.value = res.rows;
+  total.value = res.total;
+  if (userList.value && userIds.value.length > 0) {
+    const data = await optionSelect(userIds.value);
+    if (data.data && data.data.length > 0) {
+      chooseUserList.value = data.data;
+      data.data.forEach((user: UserVO) => {
+        multipleTableRef.value!.toggleRowSelection(
+          userList.value.find((item) => {
+            return item.userId == user.userId;
+          }),
+          true
+        );
+      });
+    }
+  }
+};
+
+const getDeleteMultiInstanceList = async (taskId: string) => {
+  deptOptions.value = [];
+  loading.value = true;
+  queryParams.value.taskId = taskId;
+  multiInstance.value = 'delete';
+  visible.value = true;
+  const res = await getListByDeleteMultiInstance(taskId);
+  taskList.value = res.data;
+  loading.value = false;
+};
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getAddMultiInstanceList(queryParams.value.taskId, userIds.value);
+};
+
+/** 閲嶇疆鎸夐挳鎿嶄綔 */
+const resetQuery = () => {
+  queryParams.value.pageNum = 1;
+  queryParams.value.deptId = undefined;
+  queryParams.value.userName = undefined;
+  queryParams.value.nickName = undefined;
+  deptTreeRef.value.setCurrentKey(null);
+  handleQuery();
+};
+
+/** 閫夋嫨鏉℃暟  */
+const handleSelectionChange = (selection: UserVO[]) => {
+  if (props.multiple) {
+    chooseUserList.value = selection.filter((element, index, self) => {
+      return self.findIndex((x) => x.userId === element.userId) === index;
+    });
+    selection.forEach((u) => {
+      if (chooseUserList.value && !chooseUserList.value.includes(u)) {
+        multipleTableRef.value!.toggleRowSelection(u, undefined);
+      }
+    });
+    userIds.value = chooseUserList.value.map((item) => {
+      return item.userId;
+    });
+  } else {
+    chooseUserList.value = selection;
+    if (selection.length > 1) {
+      let delRow = selection.shift();
+      multipleTableRef.value!.toggleRowSelection(delRow, undefined);
+    }
+    if (selection.length === 0) {
+      chooseUserList.value = [];
+    }
+  }
+};
+/** 閫夋嫨鏉℃暟  */
+const handleTaskSelection = (selection: any) => {
+  selectionTask.value = selection;
+};
+
+/** 鏌ヨ閮ㄩ棬涓嬫媺鏍戠粨鏋� */
+const getTreeSelect = async () => {
+  const res = await deptTreeSelect();
+  deptOptions.value = res.data;
+};
+
+/** 閫氳繃鏉′欢杩囨护鑺傜偣  */
+const filterNode = (value: string, data: any) => {
+  if (!value) return true;
+  return data.label.indexOf(value) !== -1;
+};
+/** 鏍规嵁鍚嶇О绛涢�夐儴闂ㄦ爲 */
+watchEffect(
+  () => {
+    if (visible.value && deptOptions.value && deptOptions.value.length > 0) {
+      deptTreeRef.value.filter(deptName.value);
+    }
+  },
+  {
+    flush: 'post' // watchEffect浼氬湪DOM鎸傝浇鎴栬�呮洿鏂颁箣鍓嶅氨浼氳Е鍙戯紝姝ゅ睘鎬ф帶鍒跺湪DOM鍏冪礌鏇存柊鍚庤繍琛�
+  }
+);
+/** 鑺傜偣鍗曞嚮浜嬩欢 */
+const handleNodeClick = (data: DeptVO) => {
+  queryParams.value.deptId = data.id;
+  getList();
+};
+//鍒犻櫎tag
+const handleCloseTag = (user: UserVO, index: any) => {
+  if (multipleTableRef.value.selection && multipleTableRef.value.selection.length > 0) {
+    multipleTableRef.value.selection.forEach((u: UserVO, i: Number) => {
+      if (user.userId === u.userId) {
+        multipleTableRef.value.selection.splice(i, 1);
+      }
+    });
+  }
+  if (chooseUserList.value && chooseUserList.value.length > 0) {
+    chooseUserList.value.splice(index, 1);
+  }
+  multipleTableRef.value.toggleRowSelection(user, undefined);
+
+  if (userIds.value && userIds.value.length > 0) {
+    userIds.value.forEach((userId, i) => {
+      if (userId === user.userId) {
+        userIds.value.splice(i, 1);
+      }
+    });
+  }
+};
+const submitFileForm = async () => {
+  if (multiInstance.value === 'add') {
+    if (chooseUserList.value && chooseUserList.value.length > 0) {
+      loading.value = true;
+      let userIds = chooseUserList.value.map((item) => {
+        return item.userId;
+      });
+      let nickNames = chooseUserList.value.map((item) => {
+        return item.nickName;
+      });
+      let params = {
+        taskId: queryParams.value.taskId,
+        assignees: userIds,
+        assigneeNames: nickNames
+      };
+      await addMultiInstanceExecution(params);
+      emits('submitCallback');
+      loading.value = false;
+      proxy?.$modal.msgSuccess('鎿嶄綔鎴愬姛');
+      visible.value = false;
+    }
+  } else {
+    if (selectionTask.value && selectionTask.value.length > 0) {
+      loading.value = true;
+      let taskIds = selectionTask.value.map((item: any) => {
+        return item.id;
+      });
+      let executionIds = selectionTask.value.map((item: any) => {
+        return item.executionId;
+      });
+      let assigneeIds = selectionTask.value.map((item: any) => {
+        return item.assignee;
+      });
+      let assigneeNames = selectionTask.value.map((item: any) => {
+        return item.assigneeName;
+      });
+      let params = {
+        taskId: queryParams.value.taskId,
+        taskIds: taskIds,
+        executionIds: executionIds,
+        assigneeIds: assigneeIds,
+        assigneeNames: assigneeNames
+      };
+      await deleteMultiInstanceExecution(params);
+      emits('submitCallback');
+      loading.value = false;
+      proxy?.$modal.msgSuccess('鎿嶄綔鎴愬姛');
+      visible.value = false;
+    }
+  }
+};
+//浜嬩欢
+const emits = defineEmits(['submitCallback']);
+
+/**
+ * 瀵瑰鏆撮湶瀛愮粍浠舵柟娉�
+ */
+defineExpose({
+  getAddMultiInstanceList,
+  getDeleteMultiInstanceList
+});
+</script>
diff --git a/src/components/Process/submitVerify.vue b/src/components/Process/submitVerify.vue
new file mode 100644
index 0000000..d71b855
--- /dev/null
+++ b/src/components/Process/submitVerify.vue
@@ -0,0 +1,353 @@
+<template>
+  <el-dialog v-model="dialog.visible" :title="dialog.title" width="50%" draggable :before-close="cancel" center :close-on-click-modal="false">
+    <el-form v-loading="loading" :model="form" label-width="120px">
+      <el-form-item label="娑堟伅鎻愰啋">
+        <el-checkbox-group v-model="form.messageType">
+          <el-checkbox label="1" name="type" disabled>绔欏唴淇�</el-checkbox>
+          <el-checkbox label="2" name="type">閭欢</el-checkbox>
+          <el-checkbox label="3" name="type">鐭俊</el-checkbox>
+        </el-checkbox-group>
+      </el-form-item>
+      <el-form-item label="闄勪欢" v-if="task.businessStatus === 'waiting'">
+        <fileUpload v-model="form.fileId" :fileType="['doc', 'xls', 'ppt', 'txt', 'pdf', 'xlsx', 'docx', 'zip']" :fileSize="'20'"/>
+      </el-form-item>
+      <el-form-item label="鎶勯��">
+        <el-button type="primary" @click="openUserSelectCopy" icon="Plus" circle />
+          <el-tag v-for="user in selectCopyUserList" :key="user.userId" closable style="margin: 2px" @close="handleCopyCloseTag(user)">
+            {{ user.userName }}
+          </el-tag>
+      </el-form-item>
+      <el-form-item label="瀹℃壒鎰忚" v-if="task.businessStatus === 'waiting'">
+        <el-input v-model="form.message" type="textarea" resize="none" />
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <span class="dialog-footer">
+        <el-button v-loading="buttonLoading" type="primary" @click="handleCompleteTask"> 鎻愪氦 </el-button>
+        <el-button v-if="task.businessStatus === 'waiting'" v-loading="buttonLoading" type="primary" @click="openDelegateTask"> 濮旀墭 </el-button>
+        <el-button v-if="task.businessStatus === 'waiting'" v-loading="buttonLoading" type="primary" @click="openTransferTask"> 杞姙 </el-button>
+        <el-button v-if="task.businessStatus === 'waiting' && task.multiInstance" v-loading="buttonLoading" type="primary" @click="addMultiInstanceUser"> 鍔犵 </el-button>
+        <el-button v-if="task.businessStatus === 'waiting' && task.multiInstance" v-loading="buttonLoading" type="primary" @click="deleteMultiInstanceUser"> 鍑忕 </el-button>
+        <el-button v-if="task.businessStatus === 'waiting'" v-loading="buttonLoading" type="danger" @click="handleTerminationTask"> 缁堟 </el-button>
+        <el-button v-if="task.businessStatus === 'waiting'" v-loading="buttonLoading" type="danger" @click="handleBackProcessOpen"> 閫�鍥� </el-button>
+        <el-button v-loading="buttonLoading" @click="cancel">鍙栨秷</el-button>
+      </span>
+    </template>
+    <!-- 鎶勯�� -->
+    <UserSelect ref="userSelectCopyRef" :multiple="true" :data="selectCopyUserIds" @confirm-call-back="userSelectCopyCallBack"></UserSelect>
+    <!-- 杞姙 -->
+    <UserSelect ref="transferTaskRef" :multiple="false" @confirm-call-back="handleTransferTask"></UserSelect>
+    <!-- 濮旀墭 -->
+    <UserSelect ref="delegateTaskRef" :multiple="false" @confirm-call-back="handleDelegateTask"></UserSelect>
+    <!-- 鍔犵缁勪欢 -->
+    <multiInstanceUser ref="multiInstanceUserRef" :title="title" @submit-callback='closeDialog' />
+
+    <!-- 椹冲洖寮�濮� -->
+    <el-dialog v-model="backVisible" draggable title="椹冲洖" width="40%" :close-on-click-modal="false">
+      <el-form v-loading="backLoading" :model="backForm" label-width="120px" v-if="task.businessStatus === 'waiting'">
+        <el-form-item label="椹冲洖鑺傜偣">
+          <el-select clearable placeholder="璇烽�夋嫨" v-model="backForm.targetActivityId" style="width: 300px">
+            <el-option
+              v-for="item in taskNodeList"
+              :key="item.nodeId"
+              :label="item.nodeName"
+              :value="item.nodeId"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="娑堟伅鎻愰啋">
+          <el-checkbox-group v-model="backForm.messageType">
+            <el-checkbox label="1" name="type" disabled>绔欏唴淇�</el-checkbox>
+            <el-checkbox label="2" name="type">閭欢</el-checkbox>
+            <el-checkbox label="3" name="type">鐭俊</el-checkbox>
+          </el-checkbox-group>
+        </el-form-item>
+        <el-form-item label="瀹℃壒鎰忚">
+          <el-input v-model="backForm.message" type="textarea" resize="none" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer" style="float: right;padding-bottom: 20px;">
+          <el-button type="primary" v-loading="backLoading" @click="handleBackProcess">纭</el-button>
+          <el-button v-loading="backLoading" @click="backVisible = false">鍙栨秷</el-button>
+        </div>
+      </template>
+    </el-dialog>
+    <!-- 椹冲洖缁撴潫 -->
+  </el-dialog>
+</template>
+
+<script lang="ts" setup>
+import { ref } from 'vue';
+import { ComponentInternalInstance } from 'vue';
+import { ElForm } from 'element-plus';
+import { completeTask, backProcess, getTaskById,transferTask,terminationTask,getTaskNodeList,delegateTask } from '@/api/workflow/task';
+import UserSelect from '@/components/UserSelect';
+import MultiInstanceUser from '@/components/Process/multiInstanceUser.vue';
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+import { UserVO } from '@/api/system/user/types';
+import { TaskVO } from '@/api/workflow/task/types';
+const userSelectCopyRef = ref<InstanceType<typeof UserSelect>>();
+const transferTaskRef = ref<InstanceType<typeof UserSelect>>();
+const delegateTaskRef = ref<InstanceType<typeof UserSelect>>();
+
+  //鍔犵缁勪欢
+const multiInstanceUserRef = ref<InstanceType<typeof MultiInstanceUser>>();
+
+const props = defineProps({
+  taskVariables: {
+    type: Object as () => Record<string, any>,
+    default: {}
+  }
+});
+//閬僵灞�
+const loading = ref(true);
+//鎸夐挳
+const buttonLoading = ref(true);
+//浠诲姟id
+const taskId = ref<string>('');
+//鎶勯�佷汉
+const selectCopyUserList = ref<UserVO[]>([]);
+//鎶勯�佷汉id
+const selectCopyUserIds = ref<string>(undefined);
+// 椹冲洖鏄惁鏄剧ず
+const backVisible = ref(false);
+const backLoading = ref(true);
+// 鍙┏鍥炲緱浠诲姟鑺傜偣
+const taskNodeList = ref([]);
+//浠诲姟
+const task = ref<TaskVO>({
+  id: undefined,
+  name: undefined,
+  description: undefined,
+  priority: undefined,
+  owner: undefined,
+  assignee: undefined,
+  assigneeName: undefined,
+  processInstanceId: undefined,
+  executionId: undefined,
+  taskDefinitionId: undefined,
+  processDefinitionId: undefined,
+  endTime: undefined,
+  taskDefinitionKey: undefined,
+  dueDate: undefined,
+  category: undefined,
+  parentTaskId: undefined,
+  tenantId: undefined,
+  claimTime: undefined,
+  businessStatus: undefined,
+  businessStatusName: undefined,
+  processDefinitionName: undefined,
+  processDefinitionKey: undefined,
+  participantVo: undefined,
+  multiInstance: undefined,
+  businessKey: undefined,
+  wfNodeConfigVo: undefined
+});
+//鍔犵 鍑忕鏍囬
+const title = ref('');
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: '鎻愮ず'
+});
+
+const form = ref<Record<string, any>>({
+  taskId: undefined,
+  message: undefined,
+  variables: {},
+  messageType: ['1'],
+  wfCopyList: []
+});
+const backForm = ref<Record<string, any>>({
+  taskId: undefined,
+  targetActivityId: undefined,
+  message: undefined,
+  variables: {},
+  messageType: ['1']
+});
+const closeDialog = () => {
+  dialog.visible = false
+}
+//鎵撳紑寮圭獥
+const openDialog = (id?: string) => {
+  selectCopyUserIds.value = undefined
+  selectCopyUserList.value = []
+  form.value.fileId = undefined
+  taskId.value = id;
+  form.value.message = undefined;
+  dialog.visible = true;
+  loading.value = true;
+  buttonLoading.value = true;
+  nextTick(() => {
+    getTaskById(taskId.value).then((response) => {
+      task.value = response.data;
+      loading.value = false;
+      buttonLoading.value = false;
+    });
+  });
+};
+
+onMounted(() => {});
+const emits = defineEmits(['submitCallback', 'cancelCallback']);
+
+/** 鍔炵悊娴佺▼ */
+const handleCompleteTask = async () => {
+  form.value.taskId = taskId.value;
+  form.value.taskVariables = props.taskVariables;
+  if(selectCopyUserList && selectCopyUserList.value.length > 0){
+    let wfCopyList = []
+    selectCopyUserList.value.forEach( e=> {
+      let copyUser = {
+        userId: e.userId,
+        userName: e.nickName
+      }
+      wfCopyList.push(copyUser)
+    })
+    form.value.wfCopyList = wfCopyList
+  }
+  await proxy?.$modal.confirm('鏄惁纭鎻愪氦锛�');
+  loading.value = true;
+  buttonLoading.value = true;
+  try {
+    await completeTask(form.value);
+    dialog.visible = false;
+    emits('submitCallback');
+    proxy?.$modal.msgSuccess('鎿嶄綔鎴愬姛');
+  } finally {
+    loading.value = false
+    buttonLoading.value = false
+  }
+};
+
+/** 椹冲洖寮圭獥鎵撳紑 */
+const handleBackProcessOpen = async () => {
+  backForm.value = {}
+  backForm.value.messageType = ['1']
+  backVisible.value = true
+  backLoading.value = true
+  let data = await getTaskNodeList(task.value.processInstanceId)
+  taskNodeList.value = data.data
+  backLoading.value = false
+  backForm.value.targetActivityId = taskNodeList.value[0].nodeId
+}
+/** 椹冲洖娴佺▼ */
+const handleBackProcess = async () => {
+  backForm.value.taskId = taskId.value;
+  await proxy?.$modal.confirm('鏄惁纭椹冲洖鍒扮敵璇蜂汉锛�');
+  loading.value = true;
+  backLoading.value = true;
+  await backProcess(backForm.value).finally(() => (loading.value = false));
+  dialog.visible = false;
+  backLoading.value = false
+  emits('submitCallback');
+  proxy?.$modal.msgSuccess('鎿嶄綔鎴愬姛');
+};
+//鍙栨秷
+const cancel = async () => {
+  dialog.visible = false;
+  buttonLoading.value = false;
+  emits('cancelCallback');
+};
+//鎵撳紑鎶勯�佷汉鍛�
+const openUserSelectCopy = () => {
+  userSelectCopyRef.value.open();
+};
+//纭鎶勯�佷汉鍛�
+const userSelectCopyCallBack = (data: UserVO[]) => {
+  if(data && data.length > 0){
+    selectCopyUserList.value = data
+    selectCopyUserIds.value = selectCopyUserList.value.map((item) => item.userId).join(',');
+  }
+}
+//鍒犻櫎鎶勯�佷汉鍛�
+const handleCopyCloseTag = (user: UserVO) => {
+  const userId = user.userId;
+  // 浣跨敤split鍒犻櫎鐢ㄦ埛
+  const index = selectCopyUserList.value.findIndex((item) => item.userId === userId);
+  selectCopyUserList.value.splice(index, 1);
+  selectCopyUserIds.value = selectCopyUserList.value.map((item) => item.userId).join(',');
+};
+//鍔犵
+const addMultiInstanceUser = () => {
+  if (multiInstanceUserRef.value) {
+    title.value = '鍔犵浜哄憳';
+    multiInstanceUserRef.value.getAddMultiInstanceList(taskId.value, []);
+  }
+};
+//鍑忕
+const deleteMultiInstanceUser = () => {
+  if (multiInstanceUserRef.value) {
+    title.value = '鍑忕浜哄憳';
+    multiInstanceUserRef.value.getDeleteMultiInstanceList(taskId.value);
+  }
+};
+//鎵撳紑杞姙
+const openTransferTask = () => {
+  transferTaskRef.value.open();
+};
+//杞姙
+const handleTransferTask  = async (data) => {
+  if(data && data.length > 0){
+    let params = {
+      taskId: taskId.value,
+      userId: data[0].userId,
+      comment: form.value.message
+    }
+    await proxy?.$modal.confirm('鏄惁纭鎻愪氦锛�');
+    loading.value = true;
+    buttonLoading.value = true;
+    await transferTask(params).finally(() => (loading.value = false));
+    dialog.visible = false;
+    emits('submitCallback');
+    proxy?.$modal.msgSuccess('鎿嶄綔鎴愬姛');
+  }else{
+    proxy?.$modal.msgWarning('璇烽�夋嫨鐢ㄦ埛锛�');
+  }
+}
+
+//鎵撳紑濮旀墭
+const openDelegateTask = () => {
+  delegateTaskRef.value.open();
+};
+//濮旀墭
+const handleDelegateTask  = async (data) => {
+  if(data && data.length > 0){
+    let params = {
+      taskId: taskId.value,
+      userId: data[0].userId,
+      nickName: data[0].nickName
+    }
+    await proxy?.$modal.confirm('鏄惁纭鎻愪氦锛�');
+    loading.value = true;
+    buttonLoading.value = true;
+    await delegateTask(params).finally(() => (loading.value = false));
+    dialog.visible = false;
+    emits('submitCallback');
+    proxy?.$modal.msgSuccess('鎿嶄綔鎴愬姛');
+  }else{
+    proxy?.$modal.msgWarning('璇烽�夋嫨鐢ㄦ埛锛�');
+  }
+}
+//缁堟浠诲姟
+const handleTerminationTask  = async (data) => {
+    let params = {
+      taskId: taskId.value,
+      comment: form.value.message
+    }
+    await proxy?.$modal.confirm('鏄惁纭缁堟锛�');
+    loading.value = true;
+    buttonLoading.value = true;
+    await terminationTask(params).finally(() => (loading.value = false));
+    dialog.visible = false;
+    emits('submitCallback');
+    proxy?.$modal.msgSuccess('鎿嶄綔鎴愬姛');
+  
+}
+
+/**
+ * 瀵瑰鏆撮湶瀛愮粍浠舵柟娉�
+ */
+defineExpose({
+  openDialog
+});
+</script>
diff --git a/src/components/RightToolbar/index.vue b/src/components/RightToolbar/index.vue
index 6be382f..e8c9d65 100644
--- a/src/components/RightToolbar/index.vue
+++ b/src/components/RightToolbar/index.vue
@@ -1,13 +1,13 @@
 <template>
   <div class="top-right-btn" :style="style">
     <el-row>
-      <el-tooltip class="item" effect="dark" :content="showSearch ? '闅愯棌鎼滅储' : '鏄剧ず鎼滅储'" placement="top" v-if="search">
+      <el-tooltip v-if="search" class="item" effect="dark" :content="showSearch ? '闅愯棌鎼滅储' : '鏄剧ず鎼滅储'" placement="top">
         <el-button circle icon="Search" @click="toggleSearch()" />
       </el-tooltip>
       <el-tooltip class="item" effect="dark" content="鍒锋柊" placement="top">
         <el-button circle icon="Refresh" @click="refresh()" />
       </el-tooltip>
-      <el-tooltip class="item" effect="dark" content="鏄剧ず/闅愯棌鍒�" placement="top" v-if="columns">
+      <el-tooltip v-if="columns" class="item" effect="dark" content="鏄剧ず/闅愯棌鍒�" placement="top">
         <div class="show-btn">
           <el-popover placement="bottom" trigger="click">
             <div class="tree-header">鏄剧ず/闅愯棌鍒�</div>
@@ -15,9 +15,9 @@
               ref="columnRef"
               :data="columns"
               show-checkbox
-              @check="columnChange"
               node-key="key"
               :props="{ label: 'label', children: 'children' }"
+              @check="columnChange"
             ></el-tree>
             <template #reference>
               <el-button circle icon="Menu" />
@@ -33,51 +33,49 @@
 import { propTypes } from '@/utils/propTypes';
 
 const props = defineProps({
-    showSearch: propTypes.bool.def(true),
-    columns: {
-        type: Array as PropType<FieldOption[]>,
-    },
-    search: propTypes.bool.def(true),
-    gutter: propTypes.number.def(10),
-})
+  showSearch: propTypes.bool.def(true),
+  columns: propTypes.fieldOption,
+  search: propTypes.bool.def(true),
+  gutter: propTypes.number.def(10)
+});
 
 const columnRef = ref<ElTreeInstance>();
 const emits = defineEmits(['update:showSearch', 'queryTable']);
 
 const style = computed(() => {
-    const ret: any = {};
-    if (props.gutter) {
-        ret.marginRight = `${props.gutter / 2}px`;
-    }
-    return ret;
+  const ret: any = {};
+  if (props.gutter) {
+    ret.marginRight = `${props.gutter / 2}px`;
+  }
+  return ret;
 });
 
 // 鎼滅储
 function toggleSearch() {
-    emits("update:showSearch", !props.showSearch);
+  emits('update:showSearch', !props.showSearch);
 }
 
 // 鍒锋柊
 function refresh() {
-    emits("queryTable");
+  emits('queryTable');
 }
 
 // 鏇存敼鏁版嵁鍒楃殑鏄剧ず鍜岄殣钘�
 function columnChange(...args: any[]) {
   props.columns?.forEach((item) => {
     item.visible = args[1].checkedKeys.includes(item.key);
-  })
+  });
 }
 
 // 鏄鹃殣鍒楀垵濮嬮粯璁ら殣钘忓垪
 onMounted(() => {
-    props.columns?.forEach((item) => {
-        if (item.visible) {
-          columnRef.value?.setChecked(item.key, true, false);
-            // value.value.push(item.key);
-        }
-    })
-})
+  props.columns?.forEach((item) => {
+    if (item.visible) {
+      columnRef.value?.setChecked(item.key, true, false);
+      // value.value.push(item.key);
+    }
+  });
+});
 </script>
 
 <style lang="scss" scoped>
@@ -93,7 +91,7 @@
 .my-el-transfer {
   text-align: center;
 }
-.tree-header{
+.tree-header {
   width: 100%;
   line-height: 24px;
   text-align: center;
diff --git a/src/components/RoleSelect/index.vue b/src/components/RoleSelect/index.vue
new file mode 100644
index 0000000..6ba2076
--- /dev/null
+++ b/src/components/RoleSelect/index.vue
@@ -0,0 +1,250 @@
+<template>
+  <div>
+    <el-dialog v-model="roleDialog.visible.value" :title="roleDialog.title.value" width="80%" append-to-body>
+      <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+        <div v-show="showSearch" class="mb-[10px]">
+          <el-card shadow="hover">
+            <el-form ref="queryFormRef" :model="queryParams" :inline="true">
+              <el-form-item label="瑙掕壊鍚嶇О" prop="roleName">
+                <el-input v-model="queryParams.roleName" placeholder="璇疯緭鍏ヨ鑹插悕绉�" clearable @keyup.enter="handleQuery" />
+              </el-form-item>
+              <el-form-item label="鏉冮檺瀛楃" prop="roleKey">
+                <el-input v-model="queryParams.roleKey" 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>
+          </el-card>
+        </div>
+      </transition>
+
+      <el-card shadow="hover">
+        <template #header>
+          <el-tag v-for="role in selectRoleList" :key="role.roleId" closable style="margin: 2px" @close="handleCloseTag(role)">
+            {{ role.roleName }}
+          </el-tag>
+        </template>
+
+        <vxe-table
+          ref="tableRef"
+          height="400px"
+          border
+          show-overflow
+          :data="roleList"
+          :loading="loading"
+          :row-config="{ keyField: 'roleId' }"
+          :checkbox-config="{ reserve: true, checkRowKeys: defaultSelectRoleIds }"
+          highlight-current-row
+          @checkbox-all="handleCheckboxAll"
+          @checkbox-change="handleCheckboxChange"
+        >
+          <vxe-column type="checkbox" width="50" align="center" />
+          <vxe-column v-if="false" key="roleId" label="瑙掕壊缂栧彿" />
+          <vxe-column field="roleName" title="瑙掕壊鍚嶇О" />
+          <vxe-column field="roleKey" title="鏉冮檺瀛楃" />
+          <vxe-column field="roleSort" title="鏄剧ず椤哄簭" width="100" />
+          <vxe-column title="鐘舵��" align="center" width="100">
+            <template #default="scope">
+              <dict-tag :options="sys_normal_disable" :value="scope.row.status"></dict-tag>
+            </template>
+          </vxe-column>
+          <vxe-column field="createTime" title="鍒涘缓鏃堕棿" align="center">
+            <template #default="scope">
+              <span>{{ parseTime(scope.row.createTime) }}</span>
+            </template>
+          </vxe-column>
+        </vxe-table>
+
+        <pagination
+          v-if="total > 0"
+          v-model:total="total"
+          v-model:page="queryParams.pageNum"
+          v-model:limit="queryParams.pageSize"
+          @pagination="pageList"
+        />
+      </el-card>
+      <template #footer>
+        <el-button @click="close">鍙栨秷</el-button>
+        <el-button type="primary" @click="confirm">纭畾</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { RoleVO, RoleQuery } from '@/api/system/role/types';
+import { VxeTableInstance } from 'vxe-table';
+import useDialog from '@/hooks/useDialog';
+import api from '@/api/system/role';
+interface PropType {
+  modelValue?: RoleVO[] | RoleVO | undefined;
+  multiple?: boolean;
+  data?: string | number | (string | number)[];
+}
+const prop = withDefaults(defineProps<PropType>(), {
+  multiple: true,
+  modelValue: undefined,
+  data: undefined
+});
+const emit = defineEmits(['update:modelValue', 'confirmCallBack']);
+
+const router = useRouter();
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_normal_disable'));
+
+const roleList = ref<RoleVO[]>();
+const loading = ref(true);
+const showSearch = ref(true);
+const total = ref(0);
+const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
+const selectRoleList = ref<RoleVO[]>([]);
+
+const roleDialog = useDialog({
+  title: '瑙掕壊閫夋嫨'
+});
+
+const queryFormRef = ref<ElFormInstance>();
+const tableRef = ref<VxeTableInstance<RoleVO>>();
+
+const queryParams = ref<RoleQuery>({
+  pageNum: 1,
+  pageSize: 10,
+  roleName: '',
+  roleKey: '',
+  status: ''
+});
+
+const defaultSelectRoleIds = computed(() => computedIds(prop.data));
+
+const confirm = () => {
+  emit('update:modelValue', selectRoleList.value);
+  emit('confirmCallBack', selectRoleList.value);
+  roleDialog.closeDialog();
+};
+
+const computedIds = (data) => {
+  if (data instanceof Array) {
+    return [...data];
+  } else if (typeof data === 'string') {
+    return data.split(',');
+  } else if (typeof data === 'number') {
+    return [data];
+  } else {
+    console.warn('<RoleSelect> The data type of data should be array or string or number, but I received other');
+    return [];
+  }
+};
+
+/**
+ * 鏌ヨ瑙掕壊鍒楄〃
+ */
+const getList = () => {
+  loading.value = true;
+  api.listRole(proxy?.addDateRange(queryParams.value, dateRange.value)).then((res) => {
+    roleList.value = res.rows;
+    total.value = res.total;
+    loading.value = false;
+  });
+};
+const pageList = async () => {
+  await getList();
+  const roles = roleList.value.filter((item) => {
+    return selectRoleList.value.some((role) => role.roleId === item.roleId);
+  });
+  await tableRef.value.setCheckboxRow(roles, true);
+};
+/**
+ * 鎼滅储鎸夐挳鎿嶄綔
+ */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+};
+
+/** 閲嶇疆 */
+const resetQuery = () => {
+  dateRange.value = ['', ''];
+  queryFormRef.value?.resetFields();
+  handleQuery();
+};
+
+const handleCheckboxChange = (checked) => {
+  if (!prop.multiple && checked.checked) {
+    tableRef.value.setCheckboxRow(selectRoleList.value, false);
+    selectRoleList.value = [];
+  }
+  const row = checked.row;
+  if (checked.checked) {
+    selectRoleList.value.push(row);
+  } else {
+    selectRoleList.value = selectRoleList.value.filter((item) => {
+      return item.roleId !== row.roleId;
+    });
+  }
+};
+const handleCheckboxAll = (checked) => {
+  const rows = roleList.value;
+  if (checked.checked) {
+    rows.forEach((row) => {
+      if (!selectRoleList.value.some((item) => item.roleId === row.roleId)) {
+        selectRoleList.value.push(row);
+      }
+    });
+  } else {
+    selectRoleList.value = selectRoleList.value.filter((item) => {
+      return !rows.some((row) => row.roleId === item.roleId);
+    });
+  }
+};
+
+const handleCloseTag = (user: RoleVO) => {
+  const roleId = user.roleId;
+  // 浣跨敤split鍒犻櫎鐢ㄦ埛
+  const index = selectRoleList.value.findIndex((item) => item.roleId === roleId);
+  const rows = selectRoleList.value[index];
+  tableRef.value?.setCheckboxRow(rows, false);
+  selectRoleList.value.splice(index, 1);
+};
+/**
+ * 鍒濆鍖栭�変腑鏁版嵁
+ */
+const initSelectRole = async () => {
+  if (defaultSelectRoleIds.value.length > 0) {
+    const { data } = await api.optionSelect(defaultSelectRoleIds.value);
+    selectRoleList.value = data;
+    const users = roleList.value.filter((item) => {
+      return defaultSelectRoleIds.value.includes(String(item.roleId));
+    });
+    await nextTick(() => {
+      tableRef.value.setCheckboxRow(users, true);
+    });
+  }
+};
+const close = () => {
+  roleDialog.closeDialog();
+};
+watch(
+  () => roleDialog.visible.value,
+  (newValue: boolean) => {
+    if (newValue) {
+      initSelectRole();
+    } else {
+      tableRef.value.clearCheckboxReserve();
+      tableRef.value.clearCheckboxRow();
+      resetQuery();
+      selectRoleList.value = [];
+    }
+  }
+);
+onMounted(() => {
+  getList(); // 鍒濆鍖栧垪琛ㄦ暟鎹�
+});
+
+defineExpose({
+  open: roleDialog.openDialog,
+  close: roleDialog.closeDialog
+});
+</script>
diff --git a/src/components/RuoYiDoc/index.vue b/src/components/RuoYiDoc/index.vue
index 6c7ac0d..1e27d89 100644
--- a/src/components/RuoYiDoc/index.vue
+++ b/src/components/RuoYiDoc/index.vue
@@ -8,6 +8,6 @@
 const url = ref('https://plus-doc.dromara.org/');
 
 function goto() {
-  window.open(url.value)
+  window.open(url.value);
 }
 </script>
diff --git a/src/components/RuoYiGit/index.vue b/src/components/RuoYiGit/index.vue
index 82393eb..ad27808 100644
--- a/src/components/RuoYiGit/index.vue
+++ b/src/components/RuoYiGit/index.vue
@@ -8,6 +8,6 @@
 const url = ref('https://gitee.com/dromara/RuoYi-Vue-Plus');
 
 function goto() {
-  window.open(url.value)
+  window.open(url.value);
 }
 </script>
diff --git a/src/components/SizeSelect/index.vue b/src/components/SizeSelect/index.vue
index 058ff71..abf72cc 100644
--- a/src/components/SizeSelect/index.vue
+++ b/src/components/SizeSelect/index.vue
@@ -16,20 +16,20 @@
 </template>
 
 <script setup lang="ts">
-import useAppStore from "@/store/modules/app";
+import useAppStore from '@/store/modules/app';
 
 const appStore = useAppStore();
 const size = computed(() => appStore.size);
 
 const sizeOptions = ref([
-    { label: "杈冨ぇ", value: "large" },
-    { label: "榛樿", value: "default" },
-    { label: "绋嶅皬", value: "small" },
+  { label: '杈冨ぇ', value: 'large' },
+  { label: '榛樿', value: 'default' },
+  { label: '绋嶅皬', value: 'small' }
 ]);
 
-const handleSetSize = (size: string) => {
-    appStore.setSize(size);
-}
+const handleSetSize = (size: 'large' | 'default' | 'small') => {
+  appStore.setSize(size);
+};
 </script>
 
 <style lang="scss" scoped>
diff --git a/src/components/SvgIcon/index.vue b/src/components/SvgIcon/index.vue
index 3a7540f..05dfe87 100644
--- a/src/components/SvgIcon/index.vue
+++ b/src/components/SvgIcon/index.vue
@@ -8,17 +8,17 @@
 import { propTypes } from '@/utils/propTypes';
 
 const props = defineProps({
-    iconClass: propTypes.string.isRequired,
-    className: propTypes.string.def(''),
-    color: propTypes.string.def(''),
-})
-const iconName =  computed(() => `#icon-${props.iconClass}`);
+  iconClass: propTypes.string.isRequired,
+  className: propTypes.string.def(''),
+  color: propTypes.string.def('')
+});
+const iconName = computed(() => `#icon-${props.iconClass}`);
 const svgClass = computed(() => {
-    if (props.className) {
-        return `svg-icon ${props.className}`
-    }
-    return 'svg-icon'
-})
+  if (props.className) {
+    return `svg-icon ${props.className}`;
+  }
+  return 'svg-icon';
+});
 </script>
 
 <style scope lang="scss">
diff --git a/src/components/TopNav/index.vue b/src/components/TopNav/index.vue
index 227fccc..e93e005 100644
--- a/src/components/TopNav/index.vue
+++ b/src/components/TopNav/index.vue
@@ -1,19 +1,18 @@
 <template>
-  <el-menu :default-active="activeMenu" mode="horizontal" @select="handleSelect" :ellipsis="false">
+  <el-menu :default-active="activeMenu" mode="horizontal" :ellipsis="false" @select="handleSelect">
     <template v-for="(item, index) in topMenus">
-      <el-menu-item :style="{'--theme': theme}" :index="item.path" :key="index" v-if="index < visibleNumber"
-        ><svg-icon
-        v-if="item.meta && item.meta.icon && item.meta.icon !== '#'"
-        :icon-class="item.meta ? item.meta.icon : '' " /> {{ item.meta?.title }}</el-menu-item
+      <el-menu-item v-if="index < visibleNumber" :key="index" :style="{ '--theme': theme }" :index="item.path"
+        ><svg-icon v-if="item.meta && item.meta.icon && item.meta.icon !== '#'" :icon-class="item.meta ? item.meta.icon : ''" />
+        {{ item.meta?.title }}</el-menu-item
       >
     </template>
 
     <!-- 椤堕儴鑿滃崟瓒呭嚭鏁伴噺鎶樺彔 -->
-    <el-sub-menu :style="{'--theme': theme}" index="more" v-if="topMenus.length > visibleNumber">
+    <el-sub-menu v-if="topMenus.length > visibleNumber" :style="{ '--theme': theme }" index="more">
       <template #title>鏇村鑿滃崟</template>
       <template v-for="(item, index) in topMenus">
-        <el-menu-item :index="item.path" :key="index" v-if="index >= visibleNumber"
-          ><svg-icon :icon-class="item.meta ? item.meta.icon : '' " /> {{ item.meta?.title }}</el-menu-item
+        <el-menu-item v-if="index >= visibleNumber" :key="index" :index="item.path"
+          ><svg-icon :icon-class="item.meta ? item.meta.icon : ''" /> {{ item.meta?.title }}</el-menu-item
         >
       </template>
     </el-sub-menu>
@@ -26,7 +25,7 @@
 import useAppStore from '@/store/modules/app';
 import useSettingsStore from '@/store/modules/settings';
 import usePermissionStore from '@/store/modules/permission';
-import { RouteOption } from 'vue-router';
+import { RouteRecordRaw } from 'vue-router';
 
 // 椤堕儴鏍忓垵濮嬫暟
 const visibleNumber = ref<number>(-1);
@@ -35,86 +34,89 @@
 // 闅愯棌渚ц竟鏍忚矾鐢�
 const hideList = ['/index', '/user/profile'];
 
-const appStore = useAppStore()
-const settingsStore = useSettingsStore()
-const permissionStore = usePermissionStore()
+const appStore = useAppStore();
+const settingsStore = useSettingsStore();
+const permissionStore = usePermissionStore();
 const route = useRoute();
 const router = useRouter();
 
 // 涓婚棰滆壊
 const theme = computed(() => settingsStore.theme);
 // 鎵�鏈夌殑璺敱淇℃伅
-const routers = computed(() => permissionStore.topbarRouters);
+const routers = computed(() => permissionStore.getTopbarRoutes());
 
 // 椤堕儴鏄剧ず鑿滃崟
 const topMenus = computed(() => {
-  let topMenus:RouteOption[] = [];
+  let topMenus: RouteRecordRaw[] = [];
   routers.value.map((menu) => {
     if (menu.hidden !== true) {
       // 鍏煎椤堕儴鏍忎竴绾ц彍鍗曞唴閮ㄨ烦杞�
-      if (menu.path === "/") {
-          topMenus.push(menu.children? menu.children[0] : menu);
+      if (menu.path === '/') {
+        topMenus.push(menu.children ? menu.children[0] : menu);
       } else {
-          topMenus.push(menu);
+        topMenus.push(menu);
       }
     }
-  })
+  });
   return topMenus;
-})
+});
 
 // 璁剧疆瀛愯矾鐢�
 const childrenMenus = computed(() => {
-  let childrenMenus:RouteOption[] = [];
+  let childrenMenus: RouteRecordRaw[] = [];
   routers.value.map((router) => {
     router.children?.forEach((item) => {
       if (item.parentPath === undefined) {
-        if(router.path === "/") {
-          item.path = "/" + item.path;
+        if (router.path === '/') {
+          item.path = '/' + item.path;
         } else {
-          if(!isHttp(item.path)) {
-            item.path = router.path + "/" + item.path;
+          if (!isHttp(item.path)) {
+            item.path = router.path + '/' + item.path;
           }
         }
         item.parentPath = router.path;
       }
       childrenMenus.push(item);
-    })
-  })
+    });
+  });
   return constantRoutes.concat(childrenMenus);
-})
+});
 
 // 榛樿婵�娲荤殑鑿滃崟
 const activeMenu = computed(() => {
-  const path = route.path;
+  let path = route.path;
+  if (path === '/index') {
+    path = '/system/user';
+  }
   let activePath = path;
-  if (path !== undefined && path.lastIndexOf("/") > 0 && hideList.indexOf(path) === -1) {
+  if (path !== undefined && path.lastIndexOf('/') > 0 && hideList.indexOf(path) === -1) {
     const tmpPath = path.substring(1, path.length);
-    activePath = "/" + tmpPath.substring(0, tmpPath.indexOf("/"));
+    activePath = '/' + tmpPath.substring(0, tmpPath.indexOf('/'));
     if (!route.meta.link) {
-        appStore.toggleSideBarHide(false);
+      appStore.toggleSideBarHide(false);
     }
-  } else if(!route.children) {
+  } else if (!route.children) {
     activePath = path;
     appStore.toggleSideBarHide(true);
   }
   activeRoutes(activePath);
   return activePath;
-})
+});
 
 const setVisibleNumber = () => {
   const width = document.body.getBoundingClientRect().width / 3;
   visibleNumber.value = parseInt(String(width / 85));
-}
+};
 
 const handleSelect = (key: string) => {
   currentIndex.value = key;
-  const route = routers.value.find(item => item.path === key);
+  const route = routers.value.find((item) => item.path === key);
   if (isHttp(key)) {
     // http(s):// 璺緞鏂扮獥鍙f墦寮�
-    window.open(key, "_blank");
+    window.open(key, '_blank');
   } else if (!route || !route.children) {
     // 娌℃湁瀛愯矾鐢辫矾寰勫唴閮ㄦ墦寮�
-    const routeMenu = childrenMenus.value.find(item => item.path === key);
+    const routeMenu = childrenMenus.value.find((item) => item.path === key);
     if (routeMenu && routeMenu.query) {
       let query = JSON.parse(routeMenu.query);
       router.push({ path: key, query: query });
@@ -127,35 +129,35 @@
     activeRoutes(key);
     appStore.toggleSideBarHide(false);
   }
-}
+};
 
 const activeRoutes = (key: string) => {
-  let routes:RouteOption[] = [];
+  let routes: RouteRecordRaw[] = [];
   if (childrenMenus.value && childrenMenus.value.length > 0) {
     childrenMenus.value.map((item) => {
-      if (key == item.parentPath || (key == "index" && "" == item.path)) {
+      if (key == item.parentPath || (key == 'index' && '' == item.path)) {
         routes.push(item);
       }
     });
   }
-  if(routes.length > 0) {
+  if (routes.length > 0) {
     permissionStore.setSidebarRouters(routes);
   } else {
     appStore.toggleSideBarHide(true);
   }
   return routes;
-}
+};
 
 onMounted(() => {
-  window.addEventListener('resize', setVisibleNumber)
-})
+  window.addEventListener('resize', setVisibleNumber);
+});
 onBeforeUnmount(() => {
-  window.removeEventListener('resize', setVisibleNumber)
-})
+  window.removeEventListener('resize', setVisibleNumber);
+});
 
 onMounted(() => {
-  setVisibleNumber()
-})
+  setVisibleNumber();
+});
 </script>
 
 <style lang="scss">
@@ -168,7 +170,8 @@
   margin: 0 10px !important;
 }
 
-.topmenu-container.el-menu--horizontal > .el-menu-item.is-active, .el-menu--horizontal > .el-sub-menu.is-active .el-submenu__title {
+.topmenu-container.el-menu--horizontal > .el-menu-item.is-active,
+.el-menu--horizontal > .el-sub-menu.is-active .el-submenu__title {
   border-bottom: 2px solid #{'var(--theme)'} !important;
   color: #303133;
 }
@@ -184,7 +187,9 @@
 }
 
 /* 鑳屾櫙鑹查殣钘� */
-.topmenu-container.el-menu--horizontal>.el-menu-item:not(.is-disabled):focus, .topmenu-container.el-menu--horizontal>.el-menu-item:not(.is-disabled):hover, .topmenu-container.el-menu--horizontal>.el-submenu .el-submenu__title:hover {
+.topmenu-container.el-menu--horizontal > .el-menu-item:not(.is-disabled):focus,
+.topmenu-container.el-menu--horizontal > .el-menu-item:not(.is-disabled):hover,
+.topmenu-container.el-menu--horizontal > .el-submenu .el-submenu__title:hover {
   background-color: #ffffff !important;
 }
 
diff --git a/src/components/TreeSelect/index.vue b/src/components/TreeSelect/index.vue
index 3076804..7accd6b 100644
--- a/src/components/TreeSelect/index.vue
+++ b/src/components/TreeSelect/index.vue
@@ -1,14 +1,14 @@
 <template>
   <div class="el-tree-select">
     <el-select
-      style="width: 100%"
-      v-model="valueId"
       ref="treeSelect"
+      v-model="valueId"
+      style="width: 100%"
       :filterable="true"
       :clearable="true"
-      @clear="clearHandle"
       :filter-method="selectFilterData"
       :placeholder="placeholder"
+      @clear="clearHandle"
     >
       <el-option :value="valueId" :label="valueTitle">
         <el-tree
@@ -29,43 +29,32 @@
 </template>
 
 <script setup lang="ts">
+interface ObjMap {
+  value: string;
+  label: string;
+  children: string;
+}
+interface Props {
+  objMap: ObjMap;
+  accordion: boolean;
+  value: string | number;
+  options: any[];
+  placeholder: string;
+}
 
-const props = defineProps({
-  /* 閰嶇疆椤� */
-  objMap: {
-    type: Object,
-    default: () => {
-      return {
-        value: 'id', // ID瀛楁鍚�
-        label: 'label', // 鏄剧ず鍚嶇О
-        children: 'children' // 瀛愮骇瀛楁鍚�
-      }
-    }
+const props = withDefaults(defineProps<Props>(), {
+  objMap: () => {
+    return {
+      value: 'id',
+      label: 'label',
+      children: 'children'
+    };
   },
-  /* 鑷姩鏀惰捣 */
-  accordion: {
-    type: Boolean,
-    default: () => {
-      return false
-    }
-  },
-  /**褰撳墠鍙屽悜鏁版嵁缁戝畾鐨勫�� */
-  value: {
-    type: [String, Number],
-    default: ''
-  },
-  /**褰撳墠鐨勬暟鎹� */
-  options: {
-    type: Array,
-    default: () => []
-  },
-  /**杈撳叆妗嗗唴閮ㄧ殑鏂囧瓧 */
-  placeholder: {
-    type: String,
-    default: ''
-  }
-})
-
+  accordion: false,
+  value: '',
+  options: () => [],
+  placeholder: ''
+});
 
 const selectTree = ref<ElTreeSelectInstance>();
 
@@ -74,7 +63,7 @@
 const valueId = computed({
   get: () => props.value,
   set: (val) => {
-    emit('update:value', val)
+    emit('update:value', val);
   }
 });
 const valueTitle = ref('');
@@ -83,54 +72,54 @@
 const initHandle = () => {
   nextTick(() => {
     const selectedValue = valueId.value;
-    if (selectedValue !== null && typeof (selectedValue) !== 'undefined') {
-      const node = selectTree.value?.getNode(selectedValue)
+    if (selectedValue !== null && typeof selectedValue !== 'undefined') {
+      const node = selectTree.value?.getNode(selectedValue);
       if (node) {
-        valueTitle.value = node.data[props.objMap.label]
-        selectTree.value?.setCurrentKey(selectedValue) // 璁剧疆榛樿閫変腑
-        defaultExpandedKey.value = [selectedValue] // 璁剧疆榛樿灞曞紑
+        valueTitle.value = node.data[props.objMap.label];
+        selectTree.value?.setCurrentKey(selectedValue); // 璁剧疆榛樿閫変腑
+        defaultExpandedKey.value = [selectedValue]; // 璁剧疆榛樿灞曞紑
       }
     } else {
-      clearHandle()
+      clearHandle();
     }
-  })
-}
+  });
+};
 const handleNodeClick = (node: any) => {
-  valueTitle.value = node[props.objMap.label]
+  valueTitle.value = node[props.objMap.label];
   valueId.value = node[props.objMap.value];
   defaultExpandedKey.value = [];
-  selectTree.value?.blur()
-  selectFilterData('')
-}
+  selectTree.value?.blur();
+  selectFilterData('');
+};
 const selectFilterData = (val: any) => {
-  selectTree.value?.filter(val)
-}
+  selectTree.value?.filter(val);
+};
 const filterNode = (value: any, data: any) => {
-  if (!value) return true
-  return data[props.objMap['label']].indexOf(value) !== -1
-}
+  if (!value) return true;
+  return data[props.objMap['label']].indexOf(value) !== -1;
+};
 const clearHandle = () => {
-  valueTitle.value = ''
-  valueId.value = ''
+  valueTitle.value = '';
+  valueId.value = '';
   defaultExpandedKey.value = [];
-  clearSelected()
-}
+  clearSelected();
+};
 const clearSelected = () => {
-  const allNode = document.querySelectorAll('#tree-option .el-tree-node')
-  allNode.forEach((element) => element.classList.remove('is-current'))
-}
+  const allNode = document.querySelectorAll('#tree-option .el-tree-node');
+  allNode.forEach((element) => element.classList.remove('is-current'));
+};
 
 onMounted(() => {
-  initHandle()
-})
+  initHandle();
+});
 
 watch(valueId, () => {
   initHandle();
-})
+});
 </script>
 
 <style lang="scss" scoped>
-@import "@/assets/styles/variables.module.scss";
+@import '@/assets/styles/variables.module.scss';
 
 .el-scrollbar .el-scrollbar__view .el-select-dropdown__item {
   padding: 0;
diff --git a/src/components/UserSelect/index.vue b/src/components/UserSelect/index.vue
new file mode 100644
index 0000000..f6e552e
--- /dev/null
+++ b/src/components/UserSelect/index.vue
@@ -0,0 +1,314 @@
+<template>
+  <div>
+    <el-dialog v-model="userDialog.visible.value" :title="userDialog.title.value" width="80%" append-to-body>
+      <el-row :gutter="20">
+        <!-- 閮ㄩ棬鏍� -->
+        <el-col :lg="4" :xs="24" style="">
+          <el-card shadow="hover">
+            <el-input v-model="deptName" placeholder="璇疯緭鍏ラ儴闂ㄥ悕绉�" prefix-icon="Search" clearable />
+            <el-tree
+              ref="deptTreeRef"
+              class="mt-2"
+              node-key="id"
+              :data="deptOptions"
+              :props="{ label: 'label', children: 'children' }"
+              :expand-on-click-node="false"
+              :filter-node-method="filterNode"
+              highlight-current
+              default-expand-all
+              @node-click="handleNodeClick"
+            />
+          </el-card>
+        </el-col>
+        <el-col :lg="20" :xs="24">
+          <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+            <div v-show="showSearch" class="mb-[10px]">
+              <el-card shadow="hover">
+                <el-form ref="queryFormRef" :model="queryParams" :inline="true">
+                  <el-form-item label="鐢ㄦ埛鍚嶇О" prop="userName">
+                    <el-input v-model="queryParams.userName" placeholder="璇疯緭鍏ョ敤鎴峰悕绉�" clearable @keyup.enter="handleQuery" />
+                  </el-form-item>
+                  <el-form-item label="鎵嬫満鍙风爜" prop="phonenumber">
+                    <el-input
+                      v-model="queryParams.phonenumber"
+                      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>
+              </el-card>
+            </div>
+          </transition>
+
+          <el-card shadow="hover">
+            <template v-if="prop.multiple" #header>
+              <el-tag v-for="user in selectUserList" :key="user.userId" closable style="margin: 2px" @close="handleCloseTag(user)">
+                {{ user.userName }}
+              </el-tag>
+            </template>
+
+            <vxe-table
+              ref="tableRef"
+              height="400px"
+              border
+              show-overflow
+              :data="userList"
+              :loading="loading"
+              :row-config="{ keyField: 'userId', isHover: true }"
+              :checkbox-config="{ reserve: true, trigger: 'row', highlight: true, showHeader: prop.multiple }"
+              @checkbox-all="handleCheckboxAll"
+              @checkbox-change="handleCheckboxChange"
+            >
+              <vxe-column type="checkbox" width="50" align="center" />
+              <vxe-column key="userId" title="鐢ㄦ埛缂栧彿" align="center" field="userId" />
+              <vxe-column key="userName" title="鐢ㄦ埛鍚嶇О" align="center" field="userName" />
+              <vxe-column key="nickName" title="鐢ㄦ埛鏄电О" align="center" field="nickName" />
+              <vxe-column key="deptName" title="閮ㄩ棬" align="center" field="deptName" />
+              <vxe-column key="phonenumber" title="鎵嬫満鍙风爜" align="center" field="phonenumber" width="120" />
+              <vxe-column key="status" title="鐘舵��" align="center">
+                <template #default="scope">
+                  <dict-tag :options="sys_normal_disable" :value="scope.row.status"></dict-tag>
+                </template>
+              </vxe-column>
+
+              <vxe-column title="鍒涘缓鏃堕棿" align="center" width="160">
+                <template #default="scope">
+                  <span>{{ scope.row.createTime }}</span>
+                </template>
+              </vxe-column>
+            </vxe-table>
+
+            <pagination
+              v-show="total > 0"
+              v-model:page="queryParams.pageNum"
+              v-model:limit="queryParams.pageSize"
+              :total="total"
+              @pagination="pageList"
+            />
+          </el-card>
+        </el-col>
+      </el-row>
+
+      <template #footer>
+        <el-button @click="close">鍙栨秷</el-button>
+        <el-button type="primary" @click="confirm">纭畾</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup lang="ts">
+import api from '@/api/system/user';
+import { UserQuery, UserVO } from '@/api/system/user/types';
+import { DeptVO } from '@/api/system/dept/types';
+import { VxeTableInstance } from 'vxe-table';
+import useDialog from '@/hooks/useDialog';
+
+interface PropType {
+  modelValue?: UserVO[] | UserVO | undefined;
+  multiple?: boolean;
+  data?: string | number | (string | number)[];
+}
+const prop = withDefaults(defineProps<PropType>(), {
+  multiple: true,
+  modelValue: undefined,
+  data: undefined
+});
+const emit = defineEmits(['update:modelValue', 'confirmCallBack']);
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_normal_disable'));
+
+const userList = ref<UserVO[]>();
+const loading = ref(true);
+const showSearch = ref(true);
+const total = ref(0);
+const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
+const deptName = ref('');
+const deptOptions = ref<DeptVO[]>([]);
+const selectUserList = ref<UserVO[]>([]);
+
+const deptTreeRef = ref<ElTreeInstance>();
+const queryFormRef = ref<ElFormInstance>();
+const tableRef = ref<VxeTableInstance<UserVO>>();
+
+const userDialog = useDialog({
+  title: '鐢ㄦ埛閫夋嫨'
+});
+
+const queryParams = ref<UserQuery>({
+  pageNum: 1,
+  pageSize: 10,
+  userName: '',
+  phonenumber: '',
+  status: '',
+  deptId: '',
+  roleId: ''
+});
+
+const defaultSelectUserIds = computed(() => computedIds(prop.data));
+
+/** 鏍规嵁鍚嶇О绛涢�夐儴闂ㄦ爲 */
+watchEffect(
+  () => {
+    deptTreeRef.value?.filter(deptName.value);
+  },
+  {
+    flush: 'post' // watchEffect浼氬湪DOM鎸傝浇鎴栬�呮洿鏂颁箣鍓嶅氨浼氳Е鍙戯紝姝ゅ睘鎬ф帶鍒跺湪DOM鍏冪礌鏇存柊鍚庤繍琛�
+  }
+);
+
+const confirm = () => {
+  emit('update:modelValue', selectUserList.value);
+  emit('confirmCallBack', selectUserList.value);
+  userDialog.closeDialog();
+};
+
+const computedIds = (data) => {
+  if (data instanceof Array) {
+    return [...data];
+  } else if (typeof data === 'string') {
+    return data.split(',');
+  } else if (typeof data === 'number') {
+    return [data];
+  } else {
+    console.warn('<UserSelect> The data type of data should be array or string or number, but I received other');
+    return [];
+  }
+};
+
+/** 閫氳繃鏉′欢杩囨护鑺傜偣  */
+const filterNode = (value: string, data: any) => {
+  if (!value) return true;
+  return data.label.indexOf(value) !== -1;
+};
+
+/** 鏌ヨ閮ㄩ棬涓嬫媺鏍戠粨鏋� */
+const getTreeSelect = async () => {
+  const res = await api.deptTreeSelect();
+  deptOptions.value = res.data;
+};
+
+/** 鏌ヨ鐢ㄦ埛鍒楄〃 */
+const getList = async () => {
+  loading.value = true;
+  const res = await api.listUser(proxy?.addDateRange(queryParams.value, dateRange.value));
+  loading.value = false;
+  userList.value = res.rows;
+  total.value = res.total;
+};
+
+const pageList = async () => {
+  await getList();
+  const users = userList.value.filter((item) => {
+    return selectUserList.value.some((user) => user.userId === item.userId);
+  });
+  await tableRef.value.setCheckboxRow(users, true);
+};
+
+/** 鑺傜偣鍗曞嚮浜嬩欢 */
+const handleNodeClick = (data: DeptVO) => {
+  queryParams.value.deptId = data.id;
+  handleQuery();
+};
+
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+};
+/** 閲嶇疆鎸夐挳鎿嶄綔 */
+const resetQuery = () => {
+  dateRange.value = ['', ''];
+  queryFormRef.value?.resetFields();
+  queryParams.value.pageNum = 1;
+  queryParams.value.deptId = undefined;
+  deptTreeRef.value?.setCurrentKey(undefined);
+  handleQuery();
+};
+
+const handleCheckboxChange = (checked) => {
+  if (!prop.multiple && checked.checked) {
+    tableRef.value.setCheckboxRow(selectUserList.value, false);
+    selectUserList.value = [];
+  }
+  const row = checked.row;
+  if (checked.checked) {
+    selectUserList.value.push(row);
+  } else {
+    selectUserList.value = selectUserList.value.filter((item) => {
+      return item.userId !== row.userId;
+    });
+  }
+};
+const handleCheckboxAll = (checked) => {
+  const rows = userList.value;
+  if (checked.checked) {
+    rows.forEach((row) => {
+      if (!selectUserList.value.some((item) => item.userId === row.userId)) {
+        selectUserList.value.push(row);
+      }
+    });
+  } else {
+    selectUserList.value = selectUserList.value.filter((item) => {
+      return !rows.some((row) => row.userId === item.userId);
+    });
+  }
+};
+
+const handleCloseTag = (user: UserVO) => {
+  const userId = user.userId;
+  // 浣跨敤split鍒犻櫎鐢ㄦ埛
+  const index = selectUserList.value.findIndex((item) => item.userId === userId);
+  const rows = selectUserList.value[index];
+  tableRef.value?.setCheckboxRow(rows, false);
+  selectUserList.value.splice(index, 1);
+};
+
+const initSelectUser = async () => {
+  if (defaultSelectUserIds.value.length > 0) {
+    const { data } = await api.optionSelect(defaultSelectUserIds.value);
+    selectUserList.value = data;
+    const users = userList.value.filter((item) => {
+      return defaultSelectUserIds.value.includes(String(item.userId));
+    });
+    await nextTick(() => {
+      tableRef.value.setCheckboxRow(users, true);
+    });
+  }
+};
+const close = () => {
+  userDialog.closeDialog();
+};
+
+watch(
+  () => userDialog.visible.value,
+  (newValue: boolean) => {
+    if (newValue) {
+      initSelectUser();
+    } else {
+      tableRef.value.clearCheckboxReserve();
+      tableRef.value.clearCheckboxRow();
+      resetQuery();
+      selectUserList.value = [];
+    }
+  }
+);
+
+onMounted(() => {
+  getTreeSelect(); // 鍒濆鍖栭儴闂ㄦ暟鎹�
+  getList(); // 鍒濆鍖栧垪琛ㄦ暟鎹�
+});
+
+defineExpose({
+  open: userDialog.openDialog,
+  close: userDialog.closeDialog
+});
+</script>
+
+<style lang="scss" scoped></style>
diff --git a/src/components/iFrame/index.vue b/src/components/iFrame/index.vue
index 6679f08..98f2224 100644
--- a/src/components/iFrame/index.vue
+++ b/src/components/iFrame/index.vue
@@ -9,18 +9,18 @@
 
 const props = defineProps({
   src: propTypes.string.isRequired
-})
+});
 
-const height = ref(document.documentElement.clientHeight - 94.5 + "px;")
-const loading = ref(true)
-const url = computed(() => props.src)
+const height = ref(document.documentElement.clientHeight - 94.5 + 'px;');
+const loading = ref(true);
+const url = computed(() => props.src);
 
 onMounted(() => {
   setTimeout(() => {
     loading.value = false;
   }, 300);
   window.onresize = function temp() {
-    height.value = document.documentElement.clientHeight - 94.5 + "px;";
+    height.value = document.documentElement.clientHeight - 94.5 + 'px;';
   };
-})
+});
 </script>
diff --git a/src/directive/common/copyText.ts b/src/directive/common/copyText.ts
index b6805f8..0e605d3 100644
--- a/src/directive/common/copyText.ts
+++ b/src/directive/common/copyText.ts
@@ -2,9 +2,10 @@
  * v-copyText 澶嶅埗鏂囨湰鍐呭
  * Copyright (c) 2022 ruoyi
  */
+import { DirectiveBinding } from 'vue';
 
 export default {
-  beforeMount(el: any, { value, arg }: any) {
+  beforeMount(el: any, { value, arg }: DirectiveBinding) {
     if (arg === 'callback') {
       el.$copyCallback = value;
     } else {
diff --git a/src/directive/permission/index.ts b/src/directive/permission/index.ts
index 7e9ddeb..afde332 100644
--- a/src/directive/permission/index.ts
+++ b/src/directive/permission/index.ts
@@ -9,7 +9,7 @@
     // 銆屽叾浠栬鑹层�嶆寜閽潈闄愭牎楠�
     const { value } = binding;
     if (value && value instanceof Array && value.length > 0) {
-      const hasPermission = permissions.some((permi) => {
+      const hasPermission = permissions.some((permi: string) => {
         return permi === '*:*:*' || value.includes(permi);
       });
       if (!hasPermission) {
@@ -30,7 +30,7 @@
     const { value } = binding;
     const { roles } = useUserStore();
     if (value && value instanceof Array && value.length > 0) {
-      const hasRole = roles.some((role) => {
+      const hasRole = roles.some((role: string) => {
         return role === 'admin' || value.includes(role);
       });
       if (!hasRole) {
diff --git a/src/enums/LanguageEnum.ts b/src/enums/LanguageEnum.ts
new file mode 100644
index 0000000..c857c43
--- /dev/null
+++ b/src/enums/LanguageEnum.ts
@@ -0,0 +1,5 @@
+export enum LanguageEnum {
+  zh_CN = 'zh_CN',
+
+  en_US = 'en_US'
+}
diff --git a/src/enums/bpmn/IndexEnums.ts b/src/enums/bpmn/IndexEnums.ts
new file mode 100644
index 0000000..8c39823
--- /dev/null
+++ b/src/enums/bpmn/IndexEnums.ts
@@ -0,0 +1,17 @@
+export enum AllocationTypeEnum {
+  USER = 'user',
+  CANDIDATE = 'candidate',
+  YOURSELF = 'yourself',
+  SPECIFY = 'specify'
+}
+
+export enum SpecifyDescEnum {
+  SPECIFY_MULTIPLE = 'specifyMultiple',
+  SPECIFY_SINGLE = 'specifySingle'
+}
+
+export enum MultiInstanceTypeEnum {
+  SERIAL = 'serial',
+  PARALLEL = 'parallel',
+  NONE = 'none'
+}
diff --git a/src/hooks/useDialog.ts b/src/hooks/useDialog.ts
new file mode 100644
index 0000000..68440bf
--- /dev/null
+++ b/src/hooks/useDialog.ts
@@ -0,0 +1,31 @@
+import { Ref } from 'vue';
+
+interface Options {
+  title?: string;
+}
+interface Return {
+  title: Ref<string>;
+  visible: Ref<boolean>;
+  openDialog: () => void;
+  closeDialog: () => void;
+}
+export default (ops?: Options): Return => {
+  const visible = ref(false);
+  const title = ref(ops.title || '');
+
+  const openDialog = () => {
+    visible.value = true;
+  };
+
+  const closeDialog = () => {
+    visible.value = false;
+  };
+
+  return {
+    title,
+    visible,
+
+    openDialog,
+    closeDialog
+  };
+};
\ No newline at end of file
diff --git a/src/lang/en_US.json b/src/lang/en_US.json
new file mode 100644
index 0000000..17b472e
--- /dev/null
+++ b/src/lang/en_US.json
@@ -0,0 +1,25 @@
+{
+  "route": {
+    "dashboard": "Dashboard",
+    "document": "Document"
+  },
+  "login": {
+    "username": "Username",
+    "password": "Password",
+    "login": "Login",
+    "code": "Verification Code",
+    "copyright": ""
+  },
+  "navbar": {
+    "full": "Full Screen",
+    "language": "Language",
+    "dashboard": "Dashboard",
+    "document": "Document",
+    "message": "Message",
+    "layoutSize": "Layout Size",
+    "selectTenant": "Select Tenant",
+    "layoutSetting": "Layout Setting",
+    "personalCenter": "Personal Center",
+    "logout": "Logout"
+  }
+}
diff --git a/src/lang/index.ts b/src/lang/index.ts
index dddb86b..e4e1d25 100644
--- a/src/lang/index.ts
+++ b/src/lang/index.ts
@@ -1,32 +1,24 @@
 // 鑷畾涔夊浗闄呭寲閰嶇疆
 import { createI18n } from 'vue-i18n';
 
-// 鏈湴璇█鍖�
-import enUSLocale from './en_US';
-import zhCNLocale from './zh_CN';
-
-const messages = {
-  zh_CN: {
-    ...zhCNLocale
-  },
-  en_US: {
-    ...enUSLocale
-  }
-};
+import { LanguageEnum } from '@/enums/LanguageEnum';
+import messages from '@intlify/unplugin-vue-i18n/messages';
 
 /**
  * 鑾峰彇褰撳墠璇█
  * @returns zh-cn|en ...
  */
-export const getLanguage = () => {
-  const language = useStorage('language', 'zh_CN');
+export const getLanguage = (): LanguageEnum => {
+  const language = useStorage<LanguageEnum>('language', LanguageEnum.zh_CN);
   if (language.value) {
     return language.value;
   }
-  return 'zh_CN';
+  return LanguageEnum.zh_CN;
 };
 
 const i18n = createI18n({
+  globalInjection: true,
+  allowComposition: true,
   legacy: false,
   locale: getLanguage(),
   messages
diff --git a/src/lang/zh_CN.json b/src/lang/zh_CN.json
new file mode 100644
index 0000000..071598d
--- /dev/null
+++ b/src/lang/zh_CN.json
@@ -0,0 +1,25 @@
+{
+  "route": {
+    "dashboard": "棣栭〉",
+    "document": "椤圭洰鏂囨。"
+  },
+  "login": {
+    "username": "鐢ㄦ埛鍚�",
+    "password": "瀵嗙爜",
+    "login": "鐧� 褰�",
+    "code": "璇疯緭鍏ラ獙璇佺爜",
+    "copyright": ""
+  },
+  "navbar": {
+    "full": "鍏ㄥ睆",
+    "language": "璇█",
+    "dashboard": "棣栭〉",
+    "document": "椤圭洰鏂囨。",
+    "message": "娑堟伅",
+    "layoutSize": "甯冨眬澶у皬",
+    "selectTenant": "閫夋嫨绉熸埛",
+    "layoutSetting": "甯冨眬璁剧疆",
+    "personalCenter": "涓汉涓績",
+    "logout": "閫�鍑虹櫥褰�"
+  }
+}
diff --git a/src/layout/components/AppMain.vue b/src/layout/components/AppMain.vue
index 8a73232..d634935 100644
--- a/src/layout/components/AppMain.vue
+++ b/src/layout/components/AppMain.vue
@@ -2,9 +2,12 @@
   <section class="app-main">
     <router-view v-slot="{ Component, route }">
       <transition :enter-active-class="animante" mode="out-in">
-        <keep-alive :include="tagsViewStore.cachedViews">
-          <component v-if="!route.meta.link" :is="Component" :key="route.path" />
-        </keep-alive>
+        <div>
+          <keep-alive :include="tagsViewStore.cachedViews" v-if="!route.meta.noCache">
+            <component v-if="!route.meta.link" :is="Component" :key="route.path" />
+          </keep-alive>
+          <component v-if="!route.meta.link && route.meta.noCache" :is="Component" :key="route.path" />
+        </div>
       </transition>
     </router-view>
     <iframe-toggle />
@@ -12,24 +15,28 @@
 </template>
 
 <script setup name="AppMain" lang="ts">
-import useTagsViewStore from '@/store/modules/tagsView';
 import useSettingsStore from '@/store/modules/settings';
-import IframeToggle  from './IframeToggle/index.vue'
-import { ComponentInternalInstance } from "vue";
+import useTagsViewStore from '@/store/modules/tagsView';
+
+import IframeToggle from './IframeToggle/index.vue';
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 const tagsViewStore = useTagsViewStore();
 
 // 闅忔満鍔ㄧ敾闆嗗悎
 const animante = ref<string>('');
 const animationEnable = ref(useSettingsStore().animationEnable);
-watch(()=> useSettingsStore().animationEnable, (val) => {
+watch(
+  () => useSettingsStore().animationEnable,
+  (val: boolean) => {
     animationEnable.value = val;
     if (val) {
-        animante.value = proxy?.animate.animateList[Math.round(Math.random() * proxy?.animate.animateList.length)] as string;
+      animante.value = proxy?.animate.animateList[Math.round(Math.random() * proxy?.animate.animateList.length)] as string;
     } else {
-        animante.value = proxy?.animate.defaultAnimate as string;
+      animante.value = proxy?.animate.defaultAnimate as string;
     }
-}, { immediate: true });
+  },
+  { immediate: true }
+);
 </script>
 
 <style lang="scss" scoped>
@@ -41,7 +48,7 @@
   overflow: hidden;
 }
 
-.fixed-header+.app-main {
+.fixed-header + .app-main {
   padding-top: 50px;
 }
 
@@ -51,7 +58,7 @@
     min-height: calc(100vh - 84px);
   }
 
-  .fixed-header+.app-main {
+  .fixed-header + .app-main {
     padding-top: 84px;
   }
 }
diff --git a/src/layout/components/IframeToggle/index.vue b/src/layout/components/IframeToggle/index.vue
index efb2b7a..2de529d 100644
--- a/src/layout/components/IframeToggle/index.vue
+++ b/src/layout/components/IframeToggle/index.vue
@@ -1,26 +1,27 @@
 <template>
-  <transition-group name="fade-transform" mode="out-in">
-    <inner-link
-      v-for="(item, index) in tagsViewStore.iframeViews"
-      :key="item.path"
-      :iframeId="'iframe' + index"
-      v-show="route.path === item.path"
-      :src="iframeUrl(item.meta ? item.meta.link : '', item.query)"
-    ></inner-link>
-  </transition-group>
+  <inner-link
+    v-for="(item, index) in tagsViewStore.iframeViews"
+    v-show="route.path === item.path"
+    :key="item.path"
+    :iframe-id="'iframe' + index"
+    :src="iframeUrl(item.meta ? item.meta.link : '', item.query)"
+  ></inner-link>
 </template>
 
 <script setup lang="ts">
-import InnerLink from "../InnerLink/index.vue";
+import InnerLink from '../InnerLink/index.vue';
+
 import useTagsViewStore from '@/store/modules/tagsView';
 
 const route = useRoute();
 const tagsViewStore = useTagsViewStore();
 
-function iframeUrl(url: string, query: any) {
+function iframeUrl(url: string | undefined, query: any) {
   if (Object.keys(query).length > 0) {
-    let params = Object.keys(query).map((key) => key + "=" + query[key]).join("&");
-    return url + "?" + params;
+    let params = Object.keys(query)
+      .map((key) => key + '=' + query[key])
+      .join('&');
+    return url + '?' + params;
   }
   return url;
 }
diff --git a/src/layout/components/InnerLink/index.vue b/src/layout/components/InnerLink/index.vue
index fa2869c..bb6b89b 100644
--- a/src/layout/components/InnerLink/index.vue
+++ b/src/layout/components/InnerLink/index.vue
@@ -1,18 +1,15 @@
 <template>
   <div :style="'height:' + height">
-    <iframe :id="iframeId" style="width: 100%; height: 100%" :src="src" frameborder="no"></iframe>
+    <iframe :id="iframeId" style="width: 100%; height: 100%; border: 0" :src="src"></iframe>
   </div>
 </template>
 
 <script setup lang="ts">
+import { propTypes } from '@/utils/propTypes';
+
 const props = defineProps({
-    src: {
-        type: String,
-        default: "/"
-    },
-    iframeId: {
-        type: String
-    }
+  src: propTypes.string.def('/'),
+  iframeId: propTypes.string.isRequired
 });
-const height = ref(document.documentElement.clientHeight - 94.5 + "px");
+const height = ref(document.documentElement.clientHeight - 94.5 + 'px');
 </script>
diff --git a/src/layout/components/Navbar.vue b/src/layout/components/Navbar.vue
index 7818fd4..fd4eb8c 100644
--- a/src/layout/components/Navbar.vue
+++ b/src/layout/components/Navbar.vue
@@ -1,18 +1,19 @@
 <template>
   <div class="navbar">
-    <hamburger id="hamburger-container" :is-active="appStore.sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
-    <breadcrumb id="breadcrumb-container" class="breadcrumb-container" v-if="!settingsStore.topNav" />
-    <top-nav id="topmenu-container" class="topmenu-container" v-if="settingsStore.topNav" />
+    <hamburger id="hamburger-container" :is-active="appStore.sidebar.opened" class="hamburger-container" @toggle-click="toggleSideBar" />
+    <breadcrumb v-if="!settingsStore.topNav" id="breadcrumb-container" class="breadcrumb-container" />
+    <top-nav v-if="settingsStore.topNav" id="topmenu-container" class="topmenu-container" />
 
     <div class="right-menu flex align-center">
       <template v-if="appStore.device !== 'mobile'">
         <el-select
+          v-if="userId === 1 && tenantEnabled"
           v-model="companyName"
+          class="min-w-244px"
           clearable
           filterable
           reserve-keyword
           :placeholder="$t('navbar.selectTenant')"
-          v-if="userId === 1 && tenantEnabled"
           @change="dynamicTenantEvent"
           @clear="dynamicClearEvent"
         >
@@ -63,17 +64,17 @@
         </el-tooltip>
       </template>
       <div class="avatar-container">
-        <el-dropdown @command="handleCommand" class="right-menu-item hover-effect" trigger="click">
+        <el-dropdown class="right-menu-item hover-effect" trigger="click" @command="handleCommand">
           <div class="avatar-wrapper">
             <img :src="userStore.avatar" class="user-avatar" />
             <el-icon><caret-bottom /></el-icon>
           </div>
           <template #dropdown>
             <el-dropdown-menu>
-              <router-link to="/user/profile" v-if="!dynamic">
+              <router-link v-if="!dynamic" to="/user/profile">
                 <el-dropdown-item>{{ $t('navbar.personalCenter') }}</el-dropdown-item>
               </router-link>
-              <el-dropdown-item command="setLayout" v-if="settingsStore.showSettings">
+              <el-dropdown-item v-if="settingsStore.showSettings" command="setLayout">
                 <span>{{ $t('navbar.layoutSetting') }}</span>
               </el-dropdown-item>
               <el-dropdown-item divided command="logout">
@@ -92,12 +93,11 @@
 import useAppStore from '@/store/modules/app';
 import useUserStore from '@/store/modules/user';
 import useSettingsStore from '@/store/modules/settings';
-import { getTenantList } from "@/api/login";
-import { dynamicClear, dynamicTenant } from "@/api/system/tenant";
-import { ComponentInternalInstance } from "vue";
-import { TenantVO } from "@/api/types";
-import notice from './notice/index.vue';
 import useNoticeStore from '@/store/modules/notice';
+import { getTenantList } from '@/api/login';
+import { dynamicClear, dynamicTenant } from '@/api/system/tenant';
+import { TenantVO } from '@/api/types';
+import notice from './notice/index.vue';
 
 const appStore = useAppStore();
 const userStore = useUserStore();
@@ -119,7 +119,7 @@
 
 const openSearchMenu = () => {
   searchMenuRef.value?.openSearch();
-}
+};
 
 // 鍔ㄦ�佸垏鎹�
 const dynamicTenantEvent = async (tenantId: string) => {
@@ -129,14 +129,14 @@
     proxy?.$tab.closeAllPage();
     proxy?.$router.push('/');
   }
-}
+};
 
 const dynamicClearEvent = async () => {
   await dynamicClear();
   dynamic.value = false;
   proxy?.$tab.closeAllPage();
   proxy?.$router.push('/');
-}
+};
 
 /** 绉熸埛鍒楄〃 */
 const initTenantList = async () => {
@@ -145,56 +145,58 @@
   if (tenantEnabled.value) {
     tenantList.value = data.voList;
   }
-}
+};
 
 defineExpose({
-  initTenantList,
-})
+  initTenantList
+});
 
 const toggleSideBar = () => {
   appStore.toggleSideBar(false);
-}
+};
 
 const logout = async () => {
-    await ElMessageBox.confirm('纭畾娉ㄩ攢骞堕��鍑虹郴缁熷悧锛�', '鎻愮ず', {
-      confirmButtonText: '纭畾',
-      cancelButtonText: '鍙栨秷',
-      type: 'warning'
-    })
-    await userStore.logout()
-    location.href = import.meta.env.VITE_APP_CONTEXT_PATH + 'index';
-}
+  await ElMessageBox.confirm('纭畾娉ㄩ攢骞堕��鍑虹郴缁熷悧锛�', '鎻愮ず', {
+    confirmButtonText: '纭畾',
+    cancelButtonText: '鍙栨秷',
+    type: 'warning'
+  });
+  await userStore.logout();
+  location.href = import.meta.env.VITE_APP_CONTEXT_PATH + 'index';
+};
 
-const emits = defineEmits(['setLayout'])
+const emits = defineEmits(['setLayout']);
 const setLayout = () => {
-    emits('setLayout');
-}
+  emits('setLayout');
+};
 // 瀹氫箟Command鏂规硶瀵硅薄 閫氳繃key鐩存帴璋冪敤鏂规硶
-const commandMap: {[key: string]: any} = {
-    setLayout,
-    logout
+const commandMap: { [key: string]: any } = {
+  setLayout,
+  logout
 };
 const handleCommand = (command: string) => {
-    // 鍒ゆ柇鏄惁瀛樺湪璇ユ柟娉�
-    if (commandMap[command]) {
-        commandMap[command]();
-    }
-}
-
+  // 鍒ゆ柇鏄惁瀛樺湪璇ユ柟娉�
+  if (commandMap[command]) {
+    commandMap[command]();
+  }
+};
 //鐢ㄦ繁搴︾洃鍚� 娑堟伅
-watch(() => noticeStore.state.value.notices, (newVal, oldVal) => {
-  newNotice.value = newVal.filter((item: any) => !item.read).length;
-}, { deep: true });
+watch(
+  () => noticeStore.state.value.notices,
+  (newVal) => {
+    newNotice.value = newVal.filter((item: any) => !item.read).length;
+  },
+  { deep: true }
+);
 </script>
 
 <style lang="scss" scoped>
-
 :deep(.el-select .el-input__wrapper) {
-  height:30px;
+  height: 30px;
 }
 
-:deep(.el-badge__content.is-fixed){
-    top: 12px;
+:deep(.el-badge__content.is-fixed) {
+  top: 12px;
 }
 
 .flex {
diff --git a/src/layout/components/Settings/index.vue b/src/layout/components/Settings/index.vue
index d5522ae..3082beb 100644
--- a/src/layout/components/Settings/index.vue
+++ b/src/layout/components/Settings/index.vue
@@ -1,11 +1,11 @@
 <template>
-  <el-drawer v-model="showSettings" :withHeader="false" direction="rtl" size="300px" close-on-click-modal>
+  <el-drawer v-model="showSettings" :with-header="false" direction="rtl" size="300px" close-on-click-modal>
     <h3 class="drawer-title">涓婚椋庢牸璁剧疆</h3>
 
     <div class="setting-drawer-block-checbox">
       <div class="setting-drawer-block-checbox-item" @click="handleTheme(SideThemeEnum.DARK)">
         <img src="@/assets/images/dark.svg" alt="dark" />
-        <div v-if="sideTheme === 'theme-dark'" class="setting-drawer-block-checbox-selectIcon" style="display: block;">
+        <div v-if="sideTheme === 'theme-dark'" class="setting-drawer-block-checbox-selectIcon" style="display: block">
           <i aria-label="鍥炬爣: check" class="anticon anticon-check">
             <svg viewBox="64 64 896 896" data-icon="check" width="1em" height="1em" :fill="theme" aria-hidden="true" focusable="false" class>
               <path
@@ -17,7 +17,7 @@
       </div>
       <div class="setting-drawer-block-checbox-item" @click="handleTheme(SideThemeEnum.LIGHT)">
         <img src="@/assets/images/light.svg" alt="light" />
-        <div v-if="sideTheme === 'theme-light'" class="setting-drawer-block-checbox-selectIcon" style="display: block;">
+        <div v-if="sideTheme === 'theme-light'" class="setting-drawer-block-checbox-selectIcon" style="display: block">
           <i aria-label="鍥炬爣: check" class="anticon anticon-check">
             <svg viewBox="64 64 896 896" data-icon="check" width="1em" height="1em" :fill="theme" aria-hidden="true" focusable="false" class>
               <path
@@ -37,7 +37,7 @@
     <div class="drawer-item">
       <span>娣辫壊妯″紡</span>
       <span class="comp-style">
-        <el-switch v-model="isDark" @change="toggleDark" class="drawer-switch" />
+        <el-switch v-model="isDark" class="drawer-switch" @change="toggleDark" />
       </span>
     </div>
 
@@ -48,35 +48,35 @@
     <div class="drawer-item">
       <span>寮�鍚� TopNav</span>
       <span class="comp-style">
-        <el-switch v-model="topNav" class="drawer-switch" />
+        <el-switch v-model="settingsStore.topNav" class="drawer-switch" @change="topNavChange" />
       </span>
     </div>
 
     <div class="drawer-item">
       <span>寮�鍚� Tags-Views</span>
       <span class="comp-style">
-        <el-switch v-model="tagsView" class="drawer-switch" />
+        <el-switch v-model="settingsStore.tagsView" class="drawer-switch" />
       </span>
     </div>
 
     <div class="drawer-item">
       <span>鍥哄畾 Header</span>
       <span class="comp-style">
-        <el-switch v-model="fixedHeader" class="drawer-switch" />
+        <el-switch v-model="settingsStore.fixedHeader" class="drawer-switch" />
       </span>
     </div>
 
     <div class="drawer-item">
       <span>鏄剧ず Logo</span>
       <span class="comp-style">
-        <el-switch v-model="sidebarLogo" class="drawer-switch" />
+        <el-switch v-model="settingsStore.sidebarLogo" class="drawer-switch" />
       </span>
     </div>
 
     <div class="drawer-item">
       <span>鍔ㄦ�佹爣棰�</span>
       <span class="comp-style">
-        <el-switch v-model="dynamicTitle" class="drawer-switch" />
+        <el-switch v-model="settingsStore.dynamicTitle" class="drawer-switch" @change="dynamicTitleChange" />
       </span>
     </div>
 
@@ -88,126 +88,92 @@
 </template>
 
 <script setup lang="ts">
-import { useDynamicTitle } from '@/utils/dynamicTitle'
-import useAppStore from '@/store/modules/app'
-import useSettingsStore from '@/store/modules/settings'
-import usePermissionStore from '@/store/modules/permission'
-import { handleThemeStyle } from '@/utils/theme'
-import { ComponentInternalInstance } from "vue";
-import { SettingTypeEnum } from "@/enums/SettingTypeEnum";
-import { SideThemeEnum } from "@/enums/SideThemeEnum";
+import { useDynamicTitle } from '@/utils/dynamicTitle';
+import useAppStore from '@/store/modules/app';
+import useSettingsStore from '@/store/modules/settings';
+import usePermissionStore from '@/store/modules/permission';
+import { handleThemeStyle } from '@/utils/theme';
+import { SideThemeEnum } from '@/enums/SideThemeEnum';
+import defaultSettings from '@/settings';
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
-const appStore = useAppStore()
-const settingsStore = useSettingsStore()
-const permissionStore = usePermissionStore()
-
+const appStore = useAppStore();
+const settingsStore = useSettingsStore();
+const permissionStore = usePermissionStore();
 
 const showSettings = ref(false);
 const theme = ref(settingsStore.theme);
 const sideTheme = ref(settingsStore.sideTheme);
 const storeSettings = computed(() => settingsStore);
-const predefineColors = ref(["#409EFF", "#ff4500", "#ff8c00", "#ffd700", "#90ee90", "#00ced1", "#1e90ff", "#c71585"]);
+const predefineColors = ref(['#409EFF', '#ff4500', '#ff8c00', '#ffd700', '#90ee90', '#00ced1', '#1e90ff', '#c71585']);
 
 // 鏄惁鏆楅粦妯″紡
 const isDark = useDark({
   storageKey: 'useDarkKey',
   valueDark: 'dark',
-  valueLight: 'light',
+  valueLight: 'light'
 });
-watch(isDark, ()=> {
+// 鍖归厤鑿滃崟棰滆壊
+watch(isDark, () => {
   if (isDark.value) {
-    settingsStore.changeSetting({ key: SettingTypeEnum.SIDE_THEME, value: SideThemeEnum.DARK })
+    settingsStore.sideTheme = SideThemeEnum.DARK;
   } else {
-    settingsStore.changeSetting({ key: SettingTypeEnum.SIDE_THEME, value: sideTheme.value })
+    settingsStore.sideTheme = sideTheme.value;
   }
-})
+});
 const toggleDark = () => useToggle(isDark);
 
-/** 鏄惁闇�瑕乼opNav */
-const topNav = computed({
-    get: () => storeSettings.value.topNav,
-    set: (val) => {
-        settingsStore.changeSetting({ key: SettingTypeEnum.TOP_NAV, value: val })
-        if (!val) {
-            appStore.toggleSideBarHide(false);
-            permissionStore.setSidebarRouters(permissionStore.defaultRoutes);
-        }
-    }
-})
-/** 鏄惁闇�瑕乼agview */
-const tagsView = computed({
-    get: () => storeSettings.value.tagsView,
-    set: (val) => {
-        settingsStore.changeSetting({ key: SettingTypeEnum.TAGS_VIEW, value: val })
-    }
-})
-/**鏄惁闇�瑕佸浐瀹氬ご閮� */
-const fixedHeader = computed({
-    get: () => storeSettings.value.fixedHeader,
-    set: (val) => {
-        settingsStore.changeSetting({ key: SettingTypeEnum.FIXED_HEADER, value: val })
-    }
-})
-/**鏄惁闇�瑕佷晶杈规爮鐨刲ogo */
-const sidebarLogo = computed({
-    get: () => storeSettings.value.sidebarLogo,
-    set: (val) => {
-        settingsStore.changeSetting({ key: SettingTypeEnum.SIDEBAR_LOGO, value: val })
-    }
-})
-/**鏄惁闇�瑕佷晶杈规爮鐨勫姩鎬佺綉椤电殑title */
-const dynamicTitle = computed({
-    get: () => storeSettings.value.dynamicTitle,
-    set: (val) => {
-        settingsStore.changeSetting({ key: SettingTypeEnum.DYNAMIC_TITLE, value: val })
-        // 鍔ㄦ�佽缃綉椤垫爣棰�
-        useDynamicTitle()
-    }
-})
+const topNavChange = (val: any) => {
+  if (!val) {
+    appStore.toggleSideBarHide(false);
+    permissionStore.setSidebarRouters(permissionStore.defaultRoutes as any);
+  }
+};
 
-const themeChange = (val: string | null) => {
-    settingsStore.changeSetting({ key: SettingTypeEnum.THEME, value: val })
-    theme.value = val;
-    if (val) {
-        handleThemeStyle(val);
-    }
-}
+const dynamicTitleChange = () => {
+  // 鍔ㄦ�佽缃綉椤垫爣棰�
+  useDynamicTitle();
+};
+
+const themeChange = (val: string) => {
+  settingsStore.theme = val;
+  handleThemeStyle(val);
+};
 const handleTheme = (val: string) => {
-    sideTheme.value = val;
-    if (isDark.value && val === SideThemeEnum.LIGHT) {
-      // 鏆楅粦妯″紡棰滆壊涓嶅彉
-      settingsStore.changeSetting({ key: SettingTypeEnum.SIDE_THEME, value: SideThemeEnum.DARK })
-      return
-    }
-    settingsStore.changeSetting({ key: SettingTypeEnum.SIDE_THEME, value: val })
-}
+  sideTheme.value = val;
+  if (isDark.value && val === SideThemeEnum.LIGHT) {
+    // 鏆楅粦妯″紡棰滆壊涓嶅彉
+    settingsStore.sideTheme = SideThemeEnum.DARK;
+    return;
+  }
+  settingsStore.sideTheme = val;
+};
 const saveSetting = () => {
-    proxy?.$modal.loading("姝e湪淇濆瓨鍒版湰鍦帮紝璇风◢鍊�...");
-    let layoutSetting = {
-        "topNav": storeSettings.value.topNav,
-        "tagsView": storeSettings.value.tagsView,
-        "fixedHeader": storeSettings.value.fixedHeader,
-        "sidebarLogo": storeSettings.value.sidebarLogo,
-        "dynamicTitle": storeSettings.value.dynamicTitle,
-        "sideTheme": storeSettings.value.sideTheme,
-        "theme": storeSettings.value.theme
-    };
-    localStorage.setItem("layout-setting", JSON.stringify(layoutSetting));
-    setTimeout(() => {proxy?.$modal.closeLoading()}, 1000)
-}
+  proxy?.$modal.loading('姝e湪淇濆瓨鍒版湰鍦帮紝璇风◢鍊�...');
+  const settings = useStorage<LayoutSetting>('layout-setting', defaultSettings);
+  settings.value.topNav = storeSettings.value.topNav;
+  settings.value.tagsView = storeSettings.value.tagsView;
+  settings.value.fixedHeader = storeSettings.value.fixedHeader;
+  settings.value.sidebarLogo = storeSettings.value.sidebarLogo;
+  settings.value.dynamicTitle = storeSettings.value.dynamicTitle;
+  settings.value.sideTheme = storeSettings.value.sideTheme;
+  settings.value.theme = storeSettings.value.theme;
+  setTimeout(() => {
+    proxy?.$modal.closeLoading();
+  }, 1000);
+};
 const resetSetting = () => {
-    proxy?.$modal.loading("姝e湪娓呴櫎璁剧疆缂撳瓨骞跺埛鏂帮紝璇风◢鍊�...");
-    localStorage.removeItem("layout-setting")
-    setTimeout("window.location.reload()", 1000)
-}
+  proxy?.$modal.loading('姝e湪娓呴櫎璁剧疆缂撳瓨骞跺埛鏂帮紝璇风◢鍊�...');
+  useStorage<any>('layout-setting', null).value = null;
+  setTimeout('window.location.reload()', 1000);
+};
 const openSetting = () => {
-    showSettings.value = true;
-}
+  showSettings.value = true;
+};
 
 defineExpose({
-    openSetting,
-})
+  openSetting
+});
 </script>
 
 <style lang="scss" scoped>
diff --git a/src/layout/components/Sidebar/Link.vue b/src/layout/components/Sidebar/Link.vue
index 4c6e8b3..fd75f35 100644
--- a/src/layout/components/Sidebar/Link.vue
+++ b/src/layout/components/Sidebar/Link.vue
@@ -5,36 +5,36 @@
 </template>
 
 <script setup lang="ts">
-import { isExternal } from '@/utils/validate'
+import { isExternal } from '@/utils/validate';
 
 const props = defineProps({
-    to: {
-        type: [String, Object],
-        required: true
-    }
-})
+  to: {
+    type: [String, Object],
+    required: true
+  }
+});
 
 const isExt = computed(() => {
-    return isExternal(props.to as string)
-})
+  return isExternal(props.to as string);
+});
 
 const type = computed(() => {
-    if (isExt.value) {
-        return 'a'
-    }
-    return 'router-link'
-})
+  if (isExt.value) {
+    return 'a';
+  }
+  return 'router-link';
+});
 
 function linkProps() {
-    if (isExt.value) {
-        return {
-            href: props.to,
-            target: '_blank',
-            rel: 'noopener'
-        }
-    }
+  if (isExt.value) {
     return {
-        to: props.to
-    }
+      href: props.to,
+      target: '_blank',
+      rel: 'noopener'
+    };
+  }
+  return {
+    to: props.to
+  };
 }
 </script>
diff --git a/src/layout/components/Sidebar/Logo.vue b/src/layout/components/Sidebar/Logo.vue
index a06158a..631c261 100644
--- a/src/layout/components/Sidebar/Logo.vue
+++ b/src/layout/components/Sidebar/Logo.vue
@@ -1,7 +1,7 @@
 <template>
   <div
     class="sidebar-logo-container"
-    :class="{ 'collapse': collapse }"
+    :class="{ collapse: collapse }"
     :style="{ backgroundColor: sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground }"
   >
     <transition :enter-active-class="proxy?.animate.logoAnimate.enter" mode="out-in">
@@ -22,18 +22,17 @@
 </template>
 
 <script setup lang="ts">
-import variables from '@/assets/styles/variables.module.scss'
-import logo from '@/assets/logo/logo.png'
-import useSettingsStore from '@/store/modules/settings'
-import { ComponentInternalInstance } from "vue";
+import variables from '@/assets/styles/variables.module.scss';
+import logo from '@/assets/logo/logo.png';
+import useSettingsStore from '@/store/modules/settings';
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
 defineProps({
-    collapse: {
-        type: Boolean,
-        required: true
-    }
-})
+  collapse: {
+    type: Boolean,
+    required: true
+  }
+});
 
 const title = ref('RuoYi-Vue-Plus');
 const settingsStore = useSettingsStore();
@@ -77,7 +76,12 @@
       font-weight: 600;
       line-height: 50px;
       font-size: 14px;
-      font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif;
+      font-family:
+        Avenir,
+        Helvetica Neue,
+        Arial,
+        Helvetica,
+        sans-serif;
       vertical-align: middle;
     }
   }
diff --git a/src/layout/components/Sidebar/SidebarItem.vue b/src/layout/components/Sidebar/SidebarItem.vue
index 4459cdf..3720062 100644
--- a/src/layout/components/Sidebar/SidebarItem.vue
+++ b/src/layout/components/Sidebar/SidebarItem.vue
@@ -13,7 +13,7 @@
 
     <el-sub-menu v-else ref="subMenu" :index="resolvePath(item.path)" teleported>
       <template v-if="item.meta" #title>
-        <svg-icon :icon-class="item.meta ? item.meta.icon : '' " />
+        <svg-icon :icon-class="item.meta ? item.meta.icon : ''" />
         <span class="menu-title" :title="hasTitle(item.meta?.title)">{{ item.meta?.title }}</span>
       </template>
 
@@ -30,79 +30,74 @@
 </template>
 
 <script setup lang="ts">
-import { isExternal } from '@/utils/validate'
-import AppLink from './Link.vue'
-import { getNormalPath } from '@/utils/ruoyi'
-import { RouteOption } from "vue-router";
+import { isExternal } from '@/utils/validate';
+import AppLink from './Link.vue';
+import { getNormalPath } from '@/utils/ruoyi';
+import { RouteRecordRaw } from 'vue-router';
 
 const props = defineProps({
-    // route object
-    item: {
-        type: Object as PropType<RouteOption>,
-        required: true
-    },
-    isNest: {
-        type: Boolean,
-        default: false
-    },
-    basePath: {
-        type: String,
-        default: ''
-    }
-})
+  item: {
+    type: Object as PropType<RouteRecordRaw>,
+    required: true
+  },
+  isNest: {
+    type: Boolean,
+    default: false
+  },
+  basePath: {
+    type: String,
+    default: ''
+  }
+});
 
 const onlyOneChild = ref<any>({});
 
-const hasOneShowingChild = (parent: RouteOption, children?:RouteOption[]) => {
-    if (!children) {
-        children = [];
+const hasOneShowingChild = (parent: RouteRecordRaw, children?: RouteRecordRaw[]) => {
+  if (!children) {
+    children = [];
+  }
+  const showingChildren = children.filter((item) => {
+    if (item.hidden) {
+      return false;
+    } else {
+      // Temp set(will be used if only has one showing child)
+      onlyOneChild.value = item;
+      return true;
     }
-    const showingChildren = children.filter(item => {
-        if (item.hidden) {
-            return false
-        } else {
-            // Temp set(will be used if only has one showing child)
-            onlyOneChild.value = item
-            return true
-        }
-    })
+  });
 
-    // When there is only one child router, the child router is displayed by default
-    if (showingChildren.length === 1) {
-        return true
-    }
+  // When there is only one child router, the child router is displayed by default
+  if (showingChildren.length === 1) {
+    return true;
+  }
 
-    // Show parent if there are no child router to display
-    if (showingChildren.length === 0) {
-        onlyOneChild.value = { ...parent, path: '', noShowingChildren: true }
-        if (parent.name === '2222') {
-          console.log(onlyOneChild.value)
-        }
-        return true
-    }
+  // Show parent if there are no child router to display
+  if (showingChildren.length === 0) {
+    onlyOneChild.value = { ...parent, path: '', noShowingChildren: true };
+    return true;
+  }
 
-
-    return false
+  return false;
 };
 
-const resolvePath = (routePath:string, routeQuery?:string): any => {
-    if (isExternal(routePath)) {
-        return routePath
-    }
-    if (isExternal(props.basePath)) {
-        return props.basePath
-    }
-    if (routeQuery) {
-        let query = JSON.parse(routeQuery);
-        return { path: getNormalPath(props.basePath + '/' + routePath), query: query }
-    }
-    return getNormalPath(props.basePath + '/' + routePath)
-}
+const resolvePath = (routePath: string, routeQuery?: string): any => {
+  if (isExternal(routePath)) {
+    return routePath;
+  }
+  if (isExternal(props.basePath as string)) {
+    return props.basePath;
+  }
+  if (routeQuery) {
+    let query = JSON.parse(routeQuery);
+    return { path: getNormalPath(props.basePath + '/' + routePath), query: query };
+  }
+  return getNormalPath(props.basePath + '/' + routePath);
+};
 
 const hasTitle = (title: string | undefined): string => {
-    if(!title || title.length <= 5) {
-        return "";
-    }
-    return title;
-}
+  if (!title || title.length <= 5) {
+    return '';
+  }
+  return title;
+};
 </script>
diff --git a/src/layout/components/Sidebar/index.vue b/src/layout/components/Sidebar/index.vue
index fc290b1..a4f20d0 100644
--- a/src/layout/components/Sidebar/index.vue
+++ b/src/layout/components/Sidebar/index.vue
@@ -13,7 +13,7 @@
           :collapse-transition="false"
           mode="vertical"
         >
-          <sidebar-item v-for="(route, index) in sidebarRouters" :key="route.path + index" :item="route" :base-path="route.path" />
+          <sidebar-item v-for="(r, index) in sidebarRouters" :key="r.path + index" :item="r" :base-path="r.path" />
         </el-menu>
       </transition>
     </el-scrollbar>
@@ -21,35 +21,35 @@
 </template>
 
 <script setup lang="ts">
-import Logo from './Logo.vue'
-import SidebarItem from './SidebarItem.vue'
-import variables from '@/assets/styles/variables.module.scss'
-import useAppStore from '@/store/modules/app'
-import useSettingsStore from '@/store/modules/settings'
-import usePermissionStore from '@/store/modules/permission'
-import { RouteOption } from "vue-router";
+import Logo from './Logo.vue';
+import SidebarItem from './SidebarItem.vue';
+import variables from '@/assets/styles/variables.module.scss';
+import useAppStore from '@/store/modules/app';
+import useSettingsStore from '@/store/modules/settings';
+import usePermissionStore from '@/store/modules/permission';
+import { RouteRecordRaw } from 'vue-router';
+
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
 const route = useRoute();
-const appStore = useAppStore()
-const settingsStore = useSettingsStore()
-const permissionStore = usePermissionStore()
-
-const sidebarRouters =  computed<RouteOption[]>(() => permissionStore.sidebarRouters);
+const appStore = useAppStore();
+const settingsStore = useSettingsStore();
+const permissionStore = usePermissionStore();
+const sidebarRouters = computed<RouteRecordRaw[]>(() => permissionStore.getSidebarRoutes());
 const showLogo = computed(() => settingsStore.sidebarLogo);
 const sideTheme = computed(() => settingsStore.sideTheme);
 const theme = computed(() => settingsStore.theme);
 const isCollapse = computed(() => !appStore.sidebar.opened);
 
 const activeMenu = computed(() => {
-    const { meta, path } = route;
-    // if set path, the sidebar will highlight the path you set
-    if (meta.activeMenu) {
-        return meta.activeMenu;
-    }
-    return path;
-})
+  const { meta, path } = route;
+  // if set path, the sidebar will highlight the path you set
+  if (meta.activeMenu) {
+    return meta.activeMenu;
+  }
+  return path;
+});
 
-const bgColor = computed(() => sideTheme.value === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground);
-const textColor = computed(() => sideTheme.value === 'theme-dark' ? variables.menuColor : variables.menuLightColor);
+const bgColor = computed(() => (sideTheme.value === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground));
+const textColor = computed(() => (sideTheme.value === 'theme-dark' ? variables.menuColor : variables.menuLightColor));
 </script>
diff --git a/src/layout/components/SocialCallback/index.vue b/src/layout/components/SocialCallback/index.vue
index e3a50e0..eac66bc 100644
--- a/src/layout/components/SocialCallback/index.vue
+++ b/src/layout/components/SocialCallback/index.vue
@@ -10,7 +10,6 @@
 const route = useRoute();
 const loading = ref(true);
 
-
 /**
  * 鎺ユ敹Route浼犻�掔殑鍙傛暟
  * @param {Object} route.query.
@@ -18,8 +17,7 @@
 const code = route.query.code as string;
 const state = route.query.state as string;
 const source = route.query.source as string;
-const tenantId = localStorage.getItem("tenantId") ? localStorage.getItem("tenantId") as string : '000000';
-
+const tenantId = localStorage.getItem('tenantId') ? (localStorage.getItem('tenantId') as string) : '000000';
 
 const processResponse = async (res: any) => {
   if (res.code !== 200) {
@@ -52,7 +50,6 @@
 };
 
 const loginByCode = async (data: LoginData) => {
-  console.log(2)
   try {
     const res = await login(data);
     await processResponse(res);
diff --git a/src/layout/components/TagsView/ScrollPane.vue b/src/layout/components/TagsView/ScrollPane.vue
index b50c628..3b30043 100644
--- a/src/layout/components/TagsView/ScrollPane.vue
+++ b/src/layout/components/TagsView/ScrollPane.vue
@@ -5,84 +5,85 @@
 </template>
 
 <script setup lang="ts">
-import useTagsViewStore from '@/store/modules/tagsView'
-import { TagView } from 'vue-router'
+import { RouteLocationNormalized } from 'vue-router';
+import useTagsViewStore from '@/store/modules/tagsView';
+
 const tagAndTagSpacing = ref(4);
 
-const scrollContainerRef = ref<ElScrollbarInstance>()
-const scrollWrapper = computed(() => scrollContainerRef.value?.$refs.wrapRef as any);
+const scrollContainerRef = ref<ElScrollbarInstance>();
+const scrollWrapper = computed(() => scrollContainerRef.value?.$refs.wrapRef);
 
 onMounted(() => {
-    scrollWrapper.value?.addEventListener('scroll', emitScroll, true)
-})
+  scrollWrapper.value?.addEventListener('scroll', emitScroll, true);
+});
 onBeforeUnmount(() => {
-    scrollWrapper.value?.removeEventListener('scroll', emitScroll)
-})
+  scrollWrapper.value?.removeEventListener('scroll', emitScroll);
+});
 
 const handleScroll = (e: WheelEvent) => {
-    const eventDelta = (e as any).wheelDelta || - e.deltaY * 40
-    const $scrollWrapper = scrollWrapper.value;
-    $scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4
-}
-const emits = defineEmits(['scroll'])
+  const eventDelta = (e as any).wheelDelta || -e.deltaY * 40;
+  const $scrollWrapper = scrollWrapper.value;
+  $scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4;
+};
+const emits = defineEmits(['scroll']);
 const emitScroll = () => {
-    emits('scroll')
-}
+  emits('scroll');
+};
 
-const tagsViewStore = useTagsViewStore()
+const tagsViewStore = useTagsViewStore();
 const visitedViews = computed(() => tagsViewStore.visitedViews);
 
-const moveToTarget = (currentTag: TagView) => {
-    const $container = scrollContainerRef.value?.$el
-    const $containerWidth = $container.offsetWidth
-    const $scrollWrapper = scrollWrapper.value;
+const moveToTarget = (currentTag: RouteLocationNormalized) => {
+  const $container = scrollContainerRef.value?.$el;
+  const $containerWidth = $container.offsetWidth;
+  const $scrollWrapper = scrollWrapper.value;
 
-    let firstTag = null
-    let lastTag = null
+  let firstTag = null;
+  let lastTag = null;
 
-    // find first tag and last tag
-    if (visitedViews.value.length > 0) {
-        firstTag = visitedViews.value[0]
-        lastTag = visitedViews.value[visitedViews.value.length - 1]
+  // find first tag and last tag
+  if (visitedViews.value.length > 0) {
+    firstTag = visitedViews.value[0];
+    lastTag = visitedViews.value[visitedViews.value.length - 1];
+  }
+
+  if (firstTag === currentTag) {
+    $scrollWrapper.scrollLeft = 0;
+  } else if (lastTag === currentTag) {
+    $scrollWrapper.scrollLeft = $scrollWrapper.scrollWidth - $containerWidth;
+  } else {
+    const tagListDom: any = document.getElementsByClassName('tags-view-item');
+    const currentIndex = visitedViews.value.findIndex((item) => item === currentTag);
+    let prevTag = null;
+    let nextTag = null;
+
+    for (const k in tagListDom) {
+      if (k !== 'length' && Object.hasOwnProperty.call(tagListDom, k)) {
+        if (tagListDom[k].dataset.path === visitedViews.value[currentIndex - 1].path) {
+          prevTag = tagListDom[k];
+        }
+        if (tagListDom[k].dataset.path === visitedViews.value[currentIndex + 1].path) {
+          nextTag = tagListDom[k];
+        }
+      }
     }
 
-    if (firstTag === currentTag) {
-        $scrollWrapper.scrollLeft = 0
-    } else if (lastTag === currentTag) {
-        $scrollWrapper.scrollLeft = $scrollWrapper.scrollWidth - $containerWidth
-    } else {
-        const tagListDom: any = document.getElementsByClassName('tags-view-item');
-        const currentIndex = visitedViews.value.findIndex(item => item === currentTag)
-        let prevTag = null
-        let nextTag = null
+    // the tag's offsetLeft after of nextTag
+    const afterNextTagOffsetLeft = nextTag.offsetLeft + nextTag.offsetWidth + tagAndTagSpacing.value;
 
-        for (const k in tagListDom) {
-            if (k !== 'length' && Object.hasOwnProperty.call(tagListDom, k)) {
-                if (tagListDom[k].dataset.path === visitedViews.value[currentIndex - 1].path) {
-                    prevTag = tagListDom[k];
-                }
-                if (tagListDom[k].dataset.path === visitedViews.value[currentIndex + 1].path) {
-                    nextTag = tagListDom[k];
-                }
-            }
-        }
-
-        // the tag's offsetLeft after of nextTag
-        const afterNextTagOffsetLeft = nextTag.offsetLeft + nextTag.offsetWidth + tagAndTagSpacing.value
-
-        // the tag's offsetLeft before of prevTag
-        const beforePrevTagOffsetLeft = prevTag.offsetLeft - tagAndTagSpacing.value
-        if (afterNextTagOffsetLeft > $scrollWrapper.scrollLeft + $containerWidth) {
-            $scrollWrapper.scrollLeft = afterNextTagOffsetLeft - $containerWidth
-        } else if (beforePrevTagOffsetLeft < $scrollWrapper.scrollLeft) {
-            $scrollWrapper.scrollLeft = beforePrevTagOffsetLeft
-        }
+    // the tag's offsetLeft before of prevTag
+    const beforePrevTagOffsetLeft = prevTag.offsetLeft - tagAndTagSpacing.value;
+    if (afterNextTagOffsetLeft > $scrollWrapper.scrollLeft + $containerWidth) {
+      $scrollWrapper.scrollLeft = afterNextTagOffsetLeft - $containerWidth;
+    } else if (beforePrevTagOffsetLeft < $scrollWrapper.scrollLeft) {
+      $scrollWrapper.scrollLeft = beforePrevTagOffsetLeft;
     }
-}
+  }
+};
 
 defineExpose({
-    moveToTarget,
-})
+  moveToTarget
+});
 </script>
 
 <style lang="scss" scoped>
diff --git a/src/layout/components/TagsView/index.vue b/src/layout/components/TagsView/index.vue
index 1df486e..1e12ca4 100644
--- a/src/layout/components/TagsView/index.vue
+++ b/src/layout/components/TagsView/index.vue
@@ -14,226 +14,230 @@
       >
         {{ tag.title }}
         <span v-if="!isAffix(tag)" @click.prevent.stop="closeSelectedTag(tag)">
-          <close class="el-icon-close" style="width: 1em; height: 1em;vertical-align: middle;" />
+          <close class="el-icon-close" style="width: 1em; height: 1em; vertical-align: middle" />
         </span>
       </router-link>
     </scroll-pane>
     <ul v-show="visible" :style="{ left: left + 'px', top: top + 'px' }" class="contextmenu">
-      <li @click="refreshSelectedTag(selectedTag)"><refresh-right style="width: 1em; height: 1em;" /> 鍒锋柊椤甸潰</li>
-      <li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)"><close style="width: 1em; height: 1em;" /> 鍏抽棴褰撳墠</li>
-      <li @click="closeOthersTags"><circle-close style="width: 1em; height: 1em;" /> 鍏抽棴鍏朵粬</li>
-      <li v-if="!isFirstView()" @click="closeLeftTags"><back style="width: 1em; height: 1em;" /> 鍏抽棴宸︿晶</li>
-      <li v-if="!isLastView()" @click="closeRightTags"><right style="width: 1em; height: 1em;" /> 鍏抽棴鍙充晶</li>
-      <li @click="closeAllTags(selectedTag)"><circle-close style="width: 1em; height: 1em;" /> 鍏ㄩ儴鍏抽棴</li>
+      <li @click="refreshSelectedTag(selectedTag)"><refresh-right style="width: 1em; height: 1em" /> 鍒锋柊椤甸潰</li>
+      <li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)"><close style="width: 1em; height: 1em" /> 鍏抽棴褰撳墠</li>
+      <li @click="closeOthersTags"><circle-close style="width: 1em; height: 1em" /> 鍏抽棴鍏朵粬</li>
+      <li v-if="!isFirstView()" @click="closeLeftTags"><back style="width: 1em; height: 1em" /> 鍏抽棴宸︿晶</li>
+      <li v-if="!isLastView()" @click="closeRightTags"><right style="width: 1em; height: 1em" /> 鍏抽棴鍙充晶</li>
+      <li @click="closeAllTags(selectedTag)"><circle-close style="width: 1em; height: 1em" /> 鍏ㄩ儴鍏抽棴</li>
     </ul>
   </div>
 </template>
 
 <script setup lang="ts">
-import ScrollPane from './ScrollPane.vue'
-import { getNormalPath } from '@/utils/ruoyi'
-import useTagsViewStore from "@/store/modules/tagsView";
-import useSettingsStore from '@/store/modules/settings'
-import usePermissionStore from '@/store/modules/permission'
-import { ComponentInternalInstance } from "vue";
-import { RouteOption, TagView, RouteLocationRaw } from "vue-router";
+import ScrollPane from './ScrollPane.vue';
+import { getNormalPath } from '@/utils/ruoyi';
+import useSettingsStore from '@/store/modules/settings';
+import usePermissionStore from '@/store/modules/permission';
+import useTagsViewStore from '@/store/modules/tagsView';
+import { RouteRecordRaw, RouteLocationNormalized } from 'vue-router';
 
 const visible = ref(false);
 const top = ref(0);
 const left = ref(0);
-const selectedTag = ref<TagView>({});
-const affixTags = ref<TagView[]>([]);
-const scrollPaneRef = ref(ScrollPane);
+const selectedTag = ref<RouteLocationNormalized>();
+const affixTags = ref<RouteLocationNormalized[]>([]);
+const scrollPaneRef = ref<InstanceType<typeof ScrollPane>>();
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 const route = useRoute();
 const router = useRouter();
 
-const visitedViews = computed(() => useTagsViewStore().visitedViews);
-const routes = computed(() => usePermissionStore().routes);
+const visitedViews = computed(() => useTagsViewStore().getVisitedViews());
+const routes = computed(() => usePermissionStore().getRoutes());
 const theme = computed(() => useSettingsStore().theme);
 
 watch(route, () => {
-    addTags();
-    moveToCurrentTag();
-})
+  addTags();
+  moveToCurrentTag();
+});
 watch(visible, (value) => {
-    if (value) {
-        document.body.addEventListener('click', closeMenu);
-    } else {
-        document.body.removeEventListener('click', closeMenu);
-    }
-})
+  if (value) {
+    document.body.addEventListener('click', closeMenu);
+  } else {
+    document.body.removeEventListener('click', closeMenu);
+  }
+});
 
-const isActive = (r: TagView): boolean => {
-    return r.path === route.path;
-}
-const activeStyle = (tag: TagView) => {
-    if (!isActive(tag)) return {};
-    return {
-        "background-color": theme.value,
-        "border-color": theme.value
-    };
-}
-const isAffix = (tag: TagView) => {
-    return tag.meta && tag.meta.affix;
-}
+const isActive = (r: RouteLocationNormalized): boolean => {
+  return r.path === route.path;
+};
+const activeStyle = (tag: RouteLocationNormalized) => {
+  if (!isActive(tag)) return {};
+  return {
+    'background-color': theme.value,
+    'border-color': theme.value
+  };
+};
+const isAffix = (tag: RouteLocationNormalized) => {
+  return tag?.meta && tag?.meta?.affix;
+};
 const isFirstView = () => {
-    try {
-        return selectedTag.value.fullPath === '/index' || selectedTag.value.fullPath === visitedViews.value[1].fullPath;
-    } catch (err) {
-        return false;
-    }
-}
+  try {
+    return selectedTag.value.fullPath === '/index' || selectedTag.value.fullPath === visitedViews.value[1].fullPath;
+  } catch (err) {
+    return false;
+  }
+};
 const isLastView = () => {
-    try {
-        return selectedTag.value.fullPath === visitedViews.value[visitedViews.value.length - 1].fullPath;
-    } catch (err) {
-        return false;
+  try {
+    return selectedTag.value.fullPath === visitedViews.value[visitedViews.value.length - 1].fullPath;
+  } catch (err) {
+    return false;
+  }
+};
+const filterAffixTags = (routes: RouteRecordRaw[], basePath = '') => {
+  let tags: RouteLocationNormalized[] = [];
+
+  routes.forEach((route) => {
+    if (route.meta && route.meta.affix) {
+      const tagPath = getNormalPath(basePath + '/' + route.path);
+      tags.push({
+        hash: '',
+        matched: [],
+        params: undefined,
+        query: undefined,
+        redirectedFrom: undefined,
+        fullPath: tagPath,
+        path: tagPath,
+        name: route.name as string,
+        meta: { ...route.meta }
+      });
     }
-}
-const filterAffixTags = (routes:RouteOption [], basePath = '') => {
-    let tags:TagView[] = []
-    routes.forEach(route => {
-        if (route.meta && route.meta.affix) {
-            const tagPath = getNormalPath(basePath + '/' + route.path);
-            tags.push({
-                fullPath: tagPath,
-                path: tagPath,
-                name: route.name,
-                meta: { ...route.meta }
-            })
-        }
-        if (route.children) {
-            const tempTags = filterAffixTags(route.children, route.path);
-            if (tempTags.length >= 1) {
-                tags = [...tags, ...tempTags];
-            }
-        }
-    })
-    return tags
-}
+    if (route.children) {
+      const tempTags = filterAffixTags(route.children, route.path);
+      if (tempTags.length >= 1) {
+        tags = [...tags, ...tempTags];
+      }
+    }
+  });
+  return tags;
+};
 const initTags = () => {
-    const res = filterAffixTags(routes.value);
-    affixTags.value = res;
-    for (const tag of res) {
-        // Must have tag name
-        if (tag.name) {
-            useTagsViewStore().addVisitedView(tag);
-        }
+  const res = filterAffixTags(routes.value);
+  affixTags.value = res;
+  for (const tag of res) {
+    // Must have tag name
+    if (tag.name) {
+      useTagsViewStore().addVisitedView(tag);
     }
-}
+  }
+};
 const addTags = () => {
-    const { name } = route;
-    if(route.query.title) {
-        route.meta.title = route.query.title;
-    }
-    if (name) {
-        useTagsViewStore().addView(route);
-        if (route.meta.link) {
-            useTagsViewStore().addIframeView(route);
-        }
-    }
-    return false
-}
-const moveToCurrentTag = () => {
-    nextTick(() => {
-        for (const r of visitedViews.value) {
-            if (r.path === route.path) {
-                scrollPaneRef.value.moveToTarget(r);
-                // when query is different then update
-                if (r.fullPath !== route.fullPath) {
-                    useTagsViewStore().updateVisitedView(route);
-                }
-            }
-        }
-    })
-}
-const refreshSelectedTag = (view: TagView) => {
-    proxy?.$tab.refreshPage(view);
+  const { name } = route;
+  if (route.query.title) {
+    route.meta.title = route.query.title as string;
+  }
+  if (name) {
+    useTagsViewStore().addView(route as any);
     if (route.meta.link) {
-        useTagsViewStore().delIframeView(route);
+      useTagsViewStore().addIframeView(route as any);
     }
-}
-const closeSelectedTag = (view: TagView) => {
-    proxy?.$tab.closePage(view).then(({ visitedViews }: any) => {
-        if (isActive(view)) {
-            toLastView(visitedViews, view);
+  }
+  return false;
+};
+const moveToCurrentTag = () => {
+  nextTick(() => {
+    for (const r of visitedViews.value) {
+      if (r.path === route.path) {
+        scrollPaneRef.value?.moveToTarget(r);
+        // when query is different then update
+        if (r.fullPath !== route.fullPath) {
+          useTagsViewStore().updateVisitedView(route);
         }
-    })
-}
+      }
+    }
+  });
+};
+const refreshSelectedTag = (view: RouteLocationNormalized) => {
+  proxy?.$tab.refreshPage(view);
+  if (route.meta.link) {
+    useTagsViewStore().delIframeView(route);
+  }
+};
+const closeSelectedTag = (view: RouteLocationNormalized) => {
+  proxy?.$tab.closePage(view).then(({ visitedViews }: any) => {
+    if (isActive(view)) {
+      toLastView(visitedViews, view);
+    }
+  });
+};
 const closeRightTags = () => {
-    proxy?.$tab.closeRightPage(selectedTag.value).then(visitedViews => {
-        if (!visitedViews.find(i => i.fullPath === route.fullPath)) {
-            toLastView(visitedViews);
-        }
-    })
-}
+  proxy?.$tab.closeRightPage(selectedTag.value).then((visitedViews: RouteLocationNormalized[]) => {
+    if (!visitedViews.find((i: RouteLocationNormalized) => i.fullPath === route.fullPath)) {
+      toLastView(visitedViews);
+    }
+  });
+};
 const closeLeftTags = () => {
-    proxy?.$tab.closeLeftPage(selectedTag.value).then(visitedViews => {
-        if (!visitedViews.find(i => i.fullPath === route.fullPath)) {
-            toLastView(visitedViews);
-        }
-    })
-}
+  proxy?.$tab.closeLeftPage(selectedTag.value).then((visitedViews: RouteLocationNormalized[]) => {
+    if (!visitedViews.find((i: RouteLocationNormalized) => i.fullPath === route.fullPath)) {
+      toLastView(visitedViews);
+    }
+  });
+};
 const closeOthersTags = () => {
-    router.push(selectedTag.value as RouteLocationRaw).catch(() => { });
-    proxy?.$tab.closeOtherPage(selectedTag.value).then(() => {
-        moveToCurrentTag();
-    })
-}
-const closeAllTags = (view: TagView) => {
-    proxy?.$tab.closeAllPage().then(({ visitedViews }) => {
-        if (affixTags.value.some(tag => tag.path === route.path)) {
-            return;
-        }
-        toLastView(visitedViews, view);
-    })
-}
-const toLastView = (visitedViews:TagView[], view?: TagView) => {
-    const latestView = visitedViews.slice(-1)[0];
-    if (latestView) {
-        router.push(latestView.fullPath as string);
-    } else {
-        // now the default is to redirect to the home page if there is no tags-view,
-        // you can adjust it according to your needs.
-        if (view?.name === 'Dashboard') {
-            // to reload home page
-            router.replace({ path: '/redirect' + view?.fullPath });
-        } else {
-            router.push('/');
-        }
+  router.push(selectedTag.value).catch(() => {});
+  proxy?.$tab.closeOtherPage(selectedTag.value).then(() => {
+    moveToCurrentTag();
+  });
+};
+const closeAllTags = (view: RouteLocationNormalized) => {
+  proxy?.$tab.closeAllPage().then(({ visitedViews }) => {
+    if (affixTags.value.some((tag) => tag.path === route.path)) {
+      return;
     }
-}
-const openMenu = (tag: TagView, e: MouseEvent) => {
-    const menuMinWidth = 105;
-    const offsetLeft = proxy?.$el.getBoundingClientRect().left; // container margin left
-    const offsetWidth = proxy?.$el.offsetWidth; // container width
-    const maxLeft = offsetWidth - menuMinWidth; // left boundary
-    const l = e.clientX - offsetLeft + 15; // 15: margin right
-
-    if (l > maxLeft) {
-        left.value = maxLeft;
+    toLastView(visitedViews, view);
+  });
+};
+const toLastView = (visitedViews: RouteLocationNormalized[], view?: RouteLocationNormalized) => {
+  const latestView = visitedViews.slice(-1)[0];
+  if (latestView) {
+    router.push(latestView.fullPath as string);
+  } else {
+    // now the default is to redirect to the home page if there is no tags-view,
+    // you can adjust it according to your needs.
+    if (view?.name === 'Dashboard') {
+      // to reload home page
+      router.replace({ path: '/redirect' + view?.fullPath });
     } else {
-        left.value = l;
+      router.push('/');
     }
+  }
+};
+const openMenu = (tag: RouteLocationNormalized, e: MouseEvent) => {
+  const menuMinWidth = 105;
+  const offsetLeft = proxy?.$el.getBoundingClientRect().left; // container margin left
+  const offsetWidth = proxy?.$el.offsetWidth; // container width
+  const maxLeft = offsetWidth - menuMinWidth; // left boundary
+  const l = e.clientX - offsetLeft + 15; // 15: margin right
 
-    top.value = e.clientY
-    visible.value = true;
-    selectedTag.value = tag;
-}
+  if (l > maxLeft) {
+    left.value = maxLeft;
+  } else {
+    left.value = l;
+  }
+
+  top.value = e.clientY;
+  visible.value = true;
+  selectedTag.value = tag;
+};
 const closeMenu = () => {
-    visible.value = false;
-}
+  visible.value = false;
+};
 const handleScroll = () => {
-    closeMenu();
-}
-
+  closeMenu();
+};
 
 onMounted(() => {
-    initTags();
-    addTags();
-})
+  initTags();
+  addTags();
+});
 </script>
 
 <style lang="scss" scoped>
@@ -242,7 +246,9 @@
   width: 100%;
   background-color: var(--el-bg-color);
   border: 1px solid var(--el-border-color-light);
-  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04);
+  box-shadow:
+    0 1px 3px 0 rgba(0, 0, 0, 0.12),
+    0 0 3px 0 rgba(0, 0, 0, 0.04);
   .tags-view-wrapper {
     .tags-view-item {
       display: inline-block;
@@ -271,7 +277,7 @@
         color: #fff;
         border-color: #42b983;
         &::before {
-          content: "";
+          content: '';
           background: #fff;
           display: inline-block;
           width: 8px;
diff --git a/src/layout/components/TopBar/search.vue b/src/layout/components/TopBar/search.vue
index 9dee360..cf4b024 100644
--- a/src/layout/components/TopBar/search.vue
+++ b/src/layout/components/TopBar/search.vue
@@ -3,12 +3,12 @@
     <el-dialog v-model="state.isShowSearch" destroy-on-close :show-close="false">
       <template #footer>
         <el-autocomplete
+          ref="layoutMenuAutocompleteRef"
           v-model="state.menuQuery"
           :fetch-suggestions="menuSearch"
           placeholder="鎼滅储"
-          ref="layoutMenuAutocompleteRef"
-          @select="onHandleSelect"
           :fit-input-width="true"
+          @select="onHandleSelect"
         >
           <template #prefix>
             <svg-icon class-name="search-icon" icon-class="search" />
@@ -29,130 +29,130 @@
 import { getNormalPath } from '@/utils/ruoyi';
 import { isHttp } from '@/utils/validate';
 import usePermissionStore from '@/store/modules/permission';
-import { RouteOption } from 'vue-router';
+import { RouteRecordRaw } from 'vue-router';
 type Router = Array<{
-	path: string;
-	icon: string;
-	title: string[];
-}>
+  path: string;
+  icon: string;
+  title: string[];
+}>;
 type SearchState<T = any> = {
-	isShowSearch: boolean;
-	menuQuery: string;
-	menuList: T[];
+  isShowSearch: boolean;
+  menuQuery: string;
+  menuList: T[];
 };
 // 瀹氫箟鍙橀噺鍐呭
 const layoutMenuAutocompleteRef = ref();
 const router = useRouter();
 const routes = computed(() => usePermissionStore().routes);
 const state = reactive<SearchState>({
-	isShowSearch: false,
-	menuQuery: '',
-	menuList: [],
+  isShowSearch: false,
+  menuQuery: '',
+  menuList: []
 });
 
 // 鎼滅储寮圭獥鎵撳紑
 const openSearch = () => {
-	state.menuQuery = '';
-	state.isShowSearch = true;
-	state.menuList = generateRoutes(routes.value);
-	nextTick(() => {
-		setTimeout(() => {
-			layoutMenuAutocompleteRef.value.focus();
-		});
-	});
+  state.menuQuery = '';
+  state.isShowSearch = true;
+  state.menuList = generateRoutes(routes.value as any);
+  nextTick(() => {
+    setTimeout(() => {
+      layoutMenuAutocompleteRef.value.focus();
+    });
+  });
 };
 // 鎼滅储寮圭獥鍏抽棴
 const closeSearch = () => {
-	state.isShowSearch = false;
+  state.isShowSearch = false;
 };
 // 鑿滃崟鎼滅储鏁版嵁杩囨护
 const menuSearch = (queryString: string, cb: Function) => {
-	let options = state.menuList.filter((item) => {
-		return item.title.indexOf(queryString) > -1;
-	});
-	cb(options);
+  let options = state.menuList.filter((item) => {
+    return item.title.indexOf(queryString) > -1;
+  });
+  cb(options);
 };
 
 // Filter out the routes that can be displayed in the sidebar
 // And generate the internationalized title
-const generateRoutes = (routes: RouteOption[], basePath = '', prefixTitle: string[] = []) => {
-	let res: Router = []
-	routes.forEach(r => {
-        // skip hidden router
-		if (!r.hidden) {
-			const p = r.path.length > 0 && r.path[0] === '/' ? r.path : '/' + r.path;
-				const data: any = {
-					path: !isHttp(r.path) ? getNormalPath(basePath + p) : r.path,
-					icon: r.meta?.icon,
-					title: [...prefixTitle]
-				}
-				if (r.meta && r.meta.title) {
-					data.title = [...data.title, r.meta.title];
-					if (r.redirect !== 'noRedirect') {
-						// only push the routes with title
-						// special case: need to exclude parent router without redirect
-            res.push(data);
-					}
-				}
-				// recursive child routes
-				if (r.children) {
-						const tempRoutes = generateRoutes(r.children, data.path, data.title);
-						if (tempRoutes.length >= 1) {
-								res = [...res, ...tempRoutes];
-						}
-				}
-		}
-	})
-	res.forEach((item: any) => {
-		if (item.title instanceof Array) {
-			item.title = item.title.join('/');
-		}
-	});
-	return res;
-}
+const generateRoutes = (routes: RouteRecordRaw[], basePath = '', prefixTitle: string[] = []) => {
+  let res: Router = [];
+  routes.forEach((r) => {
+    // skip hidden router
+    if (!r.hidden) {
+      const p = r.path.length > 0 && r.path[0] === '/' ? r.path : '/' + r.path;
+      const data: any = {
+        path: !isHttp(r.path) ? getNormalPath(basePath + p) : r.path,
+        icon: r.meta?.icon,
+        title: [...prefixTitle]
+      };
+      if (r.meta && r.meta.title) {
+        data.title = [...data.title, r.meta.title];
+        if (r.redirect !== 'noRedirect') {
+          // only push the routes with title
+          // special case: need to exclude parent router without redirect
+          res.push(data);
+        }
+      }
+      // recursive child routes
+      if (r.children) {
+        const tempRoutes = generateRoutes(r.children, data.path, data.title);
+        if (tempRoutes.length >= 1) {
+          res = [...res, ...tempRoutes];
+        }
+      }
+    }
+  });
+  res.forEach((item: any) => {
+    if (item.title instanceof Array) {
+      item.title = item.title.join('/');
+    }
+  });
+  return res;
+};
 // 褰撳墠鑿滃崟閫変腑鏃�
 const onHandleSelect = (val: any) => {
-	const paths = val.path;
-	if (isHttp(paths)) {
-		// http(s):// 璺緞鏂扮獥鍙f墦寮�
-		const pindex = paths.indexOf("http");
-		window.open(paths.substring(pindex, paths.length), "_blank");
-	} else {
-		router.push(paths);
-	}
-	state.menuQuery = ''
-	closeSearch();
-
+  const paths = val.path;
+  if (isHttp(paths)) {
+    // http(s):// 璺緞鏂扮獥鍙f墦寮�
+    const pindex = paths.indexOf('http');
+    window.open(paths.substring(pindex, paths.length), '_blank');
+  } else {
+    router.push(paths);
+  }
+  state.menuQuery = '';
+  closeSearch();
 };
 
 // 鏆撮湶鍙橀噺
 defineExpose({
-	openSearch
+  openSearch
 });
 </script>
 
 <style scoped lang="scss">
 .layout-search-dialog {
-	position: relative;
-	:deep(.el-dialog) {
-		.el-dialog__header,
-		.el-dialog__body {
-			display: none;
-		}
-		.el-dialog__footer {
-			width: 100%;
-			position: absolute;
-			left: 50%;
-			transform: translateX(-50%);
-			top: -53vh;
-		}
-	}
-	:deep(.el-autocomplete) {
-		width: 560px;
-		position: absolute;
-		top: 150px;
-		left: 50%;
-		transform: translateX(-50%);
-	}
+  position: relative;
+  :deep(.el-dialog) {
+    padding: 0;
+    .el-dialog__header,
+    .el-dialog__body {
+      display: none;
+    }
+    .el-dialog__footer {
+      width: 100%;
+      position: absolute;
+      left: 50%;
+      transform: translateX(-50%);
+      top: -53vh;
+    }
+  }
+  :deep(.el-autocomplete) {
+    width: 560px;
+    position: absolute;
+    top: 150px;
+    left: 50%;
+    transform: translateX(-50%);
+  }
 }
 </style>
diff --git a/src/layout/components/notice/index.vue b/src/layout/components/notice/index.vue
index ef4a6a9..e43b2b8 100644
--- a/src/layout/components/notice/index.vue
+++ b/src/layout/components/notice/index.vue
@@ -1,12 +1,12 @@
 <template>
-  <div class="layout-navbars-breadcrumb-user-news" v-loading="state.loading">
+  <div v-loading="state.loading" class="layout-navbars-breadcrumb-user-news">
     <div class="head-box">
       <div class="head-box-title">閫氱煡鍏憡</div>
       <div class="head-box-btn" @click="readAll">鍏ㄩ儴宸茶</div>
     </div>
-    <div class="content-box" v-loading="state.loading">
+    <div v-loading="state.loading" class="content-box">
       <template v-if="newsList.length > 0">
-        <div class="content-box-item" v-for="(v, k) in newsList" :key="k" @click="onNewsClick(k)">
+        <div v-for="(v, k) in newsList" :key="k" class="content-box-item" @click="onNewsClick(k)">
           <div class="item-conten">
             <div>{{ v.message }}</div>
             <div class="content-box-msg"></div>
@@ -17,26 +17,24 @@
           <span v-else class="el-tag el-tag--danger el-tag--mini read">鏈</span>
         </div>
       </template>
-      <el-empty :description="'娑堟伅涓虹┖'" v-else></el-empty>
+      <el-empty v-else :description="'娑堟伅涓虹┖'"></el-empty>
     </div>
-    <div class="foot-box" @click="onGoToGiteeClick" v-if="newsList.length > 0">鍓嶅線gitee</div>
+    <div v-if="newsList.length > 0" class="foot-box" @click="onGoToGiteeClick">鍓嶅線gitee</div>
   </div>
 </template>
 
 <script setup lang="ts" name="layoutBreadcrumbUserNews">
-import { ref } from "vue";
-import { storeToRefs } from 'pinia'
-import { nextTick, onMounted, reactive } from "vue";
+import { storeToRefs } from 'pinia';
 import useNoticeStore from '@/store/modules/notice';
 
 const noticeStore = storeToRefs(useNoticeStore());
-const {readAll} = useNoticeStore();
+const { readAll } = useNoticeStore();
 
 // 瀹氫箟鍙橀噺鍐呭
 const state = reactive({
-  loading: false,
+  loading: false
 });
-const newsList =ref([]) as any;
+const newsList = ref([]) as any;
 
 /**
  * 鍒濆鍖栨暟鎹�
@@ -48,7 +46,6 @@
   state.loading = false;
 };
 
-
 //鐐瑰嚮娑堟伅锛屽啓鍏ュ凡璇�
 const onNewsClick = (item: any) => {
   newsList.value[item].read = true;
@@ -58,7 +55,7 @@
 
 // 鍓嶅線閫氱煡涓績鐐瑰嚮
 const onGoToGiteeClick = () => {
-  window.open("https://gitee.com/dromara/RuoYi-Vue-Plus/tree/5.X/");
+  window.open('https://gitee.com/dromara/RuoYi-Vue-Plus/tree/5.X/');
 };
 
 onMounted(() => {
diff --git a/src/layout/index.vue b/src/layout/index.vue
index 8239ed7..29fb5ff 100644
--- a/src/layout/index.vue
+++ b/src/layout/index.vue
@@ -12,7 +12,7 @@
         <settings ref="settingRef" />
       </el-scrollbar> -->
       <div :class="{ 'fixed-header': fixedHeader }">
-        <navbar ref="navbarRef" @setLayout="setLayout" />
+        <navbar ref="navbarRef" @set-layout="setLayout" />
         <tags-view v-if="needTagsView" />
       </div>
       <app-main />
@@ -22,12 +22,13 @@
 </template>
 
 <script setup lang="ts">
-import SideBar from './components/Sidebar/index.vue'
-import { AppMain, Navbar, Settings, TagsView } from './components'
-import useAppStore from '@/store/modules/app'
-import useSettingsStore from '@/store/modules/settings'
+import SideBar from './components/Sidebar/index.vue';
+import { AppMain, Navbar, Settings, TagsView } from './components';
+import useAppStore from '@/store/modules/app';
+import useSettingsStore from '@/store/modules/settings';
+import { initWebSocket } from '@/utils/websocket';
 
-const settingsStore = useSettingsStore()
+const settingsStore = useSettingsStore();
 const theme = computed(() => settingsStore.theme);
 const sidebar = computed(() => useAppStore().sidebar);
 const device = computed(() => useAppStore().device);
@@ -35,48 +36,53 @@
 const fixedHeader = computed(() => settingsStore.fixedHeader);
 
 const classObj = computed(() => ({
-    hideSidebar: !sidebar.value.opened,
-    openSidebar: sidebar.value.opened,
-    withoutAnimation: sidebar.value.withoutAnimation,
-    mobile: device.value === 'mobile'
-}))
+  hideSidebar: !sidebar.value.opened,
+  openSidebar: sidebar.value.opened,
+  withoutAnimation: sidebar.value.withoutAnimation,
+  mobile: device.value === 'mobile'
+}));
 
 const { width } = useWindowSize();
 const WIDTH = 992; // refer to Bootstrap's responsive design
 
 watchEffect(() => {
-    if (device.value === 'mobile' && sidebar.value.opened) {
-        useAppStore().closeSideBar({ withoutAnimation: false })
-    }
-    if (width.value - 1 < WIDTH) {
-        useAppStore().toggleDevice('mobile')
-        useAppStore().closeSideBar({ withoutAnimation: true })
-    } else {
-        useAppStore().toggleDevice('desktop')
-    }
-})
+  if (device.value === 'mobile') {
+    useAppStore().closeSideBar({ withoutAnimation: false });
+  }
+  if (width.value - 1 < WIDTH) {
+    useAppStore().toggleDevice('mobile');
+    useAppStore().closeSideBar({ withoutAnimation: true });
+  } else {
+    useAppStore().toggleDevice('desktop');
+  }
+});
 
-const navbarRef = ref(Navbar);
-const settingRef = ref(Settings);
+const navbarRef = ref<InstanceType<typeof Navbar>>();
+const settingRef = ref<InstanceType<typeof Settings>>();
 
 onMounted(() => {
   nextTick(() => {
-    navbarRef.value.initTenantList();
-  })
-})
+    navbarRef.value?.initTenantList();
+  });
+});
+
+onMounted(() => {
+  let protocol = window.location.protocol === 'https:' ? 'wss://' : 'ws://';
+  initWebSocket(protocol + window.location.host + import.meta.env.VITE_APP_BASE_API + '/resource/websocket');
+});
 
 const handleClickOutside = () => {
-  useAppStore().closeSideBar({ withoutAnimation: false })
-}
+  useAppStore().closeSideBar({ withoutAnimation: false });
+};
 
 const setLayout = () => {
-  settingRef.value.openSetting();
-}
+  settingRef.value?.openSetting();
+};
 </script>
 
 <style lang="scss" scoped>
-  @import "@/assets/styles/mixin.scss";
-  @import "@/assets/styles/variables.module.scss";
+@import '@/assets/styles/mixin.scss';
+@import '@/assets/styles/variables.module.scss';
 
 .app-wrapper {
   @include clearfix;
diff --git a/src/main.ts b/src/main.ts
index 0ad939a..592de58 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,6 +1,6 @@
 import { createApp } from 'vue';
 // global css
-import 'uno.css';
+import 'virtual:uno.css';
 import '@/assets/styles/index.scss';
 import 'element-plus/theme-chalk/dark/css-vars.css';
 
@@ -14,10 +14,12 @@
 
 // 娉ㄥ唽鎻掍欢
 import plugins from './plugins/index'; // plugins
-import { download } from '@/utils/request';
 
-// 棰勮鍔ㄧ敾
-import animate from './animate';
+// 楂樹寒缁勪欢
+// import 'highlight.js/styles/a11y-light.css';
+import 'highlight.js/styles/atom-one-dark.css';
+import 'highlight.js/lib/common';
+import HighLight from '@highlightjs/vue-plugin';
 
 // svg鍥炬爣
 import 'virtual:svg-icons-register';
@@ -26,30 +28,28 @@
 // permission control
 import './permission';
 
-import { useDict } from '@/utils/dict';
-import { getConfigKey, updateConfigByKey } from '@/api/system/config';
-import { parseTime, addDateRange, handleTree, selectDictLabel, selectDictLabels } from '@/utils/ruoyi';
-
 // 鍥介檯鍖�
 import i18n from '@/lang/index';
 
-const app = createApp(App);
-// 鍏ㄥ眬鏂规硶鎸傝浇
-app.config.globalProperties.useDict = useDict;
-app.config.globalProperties.getConfigKey = getConfigKey;
-app.config.globalProperties.updateConfigByKey = updateConfigByKey;
-app.config.globalProperties.download = download;
-app.config.globalProperties.parseTime = parseTime;
-app.config.globalProperties.handleTree = handleTree;
-app.config.globalProperties.addDateRange = addDateRange;
-app.config.globalProperties.selectDictLabel = selectDictLabel;
-app.config.globalProperties.selectDictLabels = selectDictLabels;
-app.config.globalProperties.animate = animate;
+// vxeTable
+import VXETable from 'vxe-table';
+import 'vxe-table/lib/style.css';
+VXETable.config({
+  zIndex: 999999
+});
 
+// 淇敼 el-dialog 榛樿鐐瑰嚮閬収涓轰笉鍏抽棴
+import { ElDialog } from 'element-plus';
+ElDialog.props.closeOnClickModal.default = false;
+
+const app = createApp(App);
+
+app.use(HighLight);
 app.use(ElementIcons);
 app.use(router);
 app.use(store);
 app.use(i18n);
+app.use(VXETable);
 app.use(plugins);
 // 鑷畾涔夋寚浠�
 directive(app);
diff --git a/src/permission.ts b/src/permission.ts
index c819ff4..1123738 100644
--- a/src/permission.ts
+++ b/src/permission.ts
@@ -15,13 +15,13 @@
 router.beforeEach(async (to, from, next) => {
   NProgress.start();
   if (getToken()) {
-    to.meta.title && useSettingsStore().setTitle(to.meta.title as string);
+    to.meta.title && useSettingsStore().setTitle(to.meta.title);
     /* has token*/
     if (to.path === '/login') {
       next({ path: '/' });
       NProgress.done();
-    } else if (whiteList.indexOf(to.path) !== -1) {
-      next()
+    } else if (whiteList.indexOf(to.path as string) !== -1) {
+      next();
     } else {
       if (useUserStore().roles.length === 0) {
         isRelogin.show = true;
@@ -40,7 +40,7 @@
               router.addRoute(route); // 鍔ㄦ�佹坊鍔犲彲璁块棶璺敱琛�
             }
           });
-          next({ ...to, replace: true }); // hack鏂规硶 纭繚addRoutes宸插畬鎴�
+          next({ path: to.path, replace: true, params: to.params, query: to.query, hash: to.hash, name: to.name as string }); // hack鏂规硶 纭繚addRoutes宸插畬鎴�
         }
       } else {
         next();
@@ -48,11 +48,12 @@
     }
   } else {
     // 娌℃湁token
-    if (whiteList.indexOf(to.path) !== -1) {
+    if (whiteList.indexOf(to.path as string) !== -1) {
       // 鍦ㄥ厤鐧诲綍鐧藉悕鍗曪紝鐩存帴杩涘叆
       next();
     } else {
-      next(`/login?redirect=${to.fullPath}`); // 鍚﹀垯鍏ㄩ儴閲嶅畾鍚戝埌鐧诲綍椤�
+      const redirect = encodeURIComponent(to.fullPath || '/');
+      next(`/login?redirect=${redirect}`) // 鍚﹀垯鍏ㄩ儴閲嶅畾鍚戝埌鐧诲綍椤�
       NProgress.done();
     }
   }
diff --git a/src/plugins/index.ts b/src/plugins/index.ts
index 09d550a..6c5e0c3 100644
--- a/src/plugins/index.ts
+++ b/src/plugins/index.ts
@@ -3,6 +3,13 @@
 import download from './download';
 import cache from './cache';
 import auth from './auth';
+// 棰勮鍔ㄧ敾
+import animate from '@/animate';
+
+import { download as dl } from '@/utils/request';
+import { useDict } from '@/utils/dict';
+import { getConfigKey, updateConfigByKey } from '@/api/system/config';
+import { parseTime, addDateRange, handleTree, selectDictLabel, selectDictLabels } from '@/utils/ruoyi';
 
 import { App } from 'vue';
 
@@ -21,4 +28,16 @@
 
   // 璁よ瘉瀵硅薄
   app.config.globalProperties.$auth = auth;
+
+  // 鍏ㄥ眬鏂规硶鎸傝浇
+  app.config.globalProperties.useDict = useDict;
+  app.config.globalProperties.getConfigKey = getConfigKey;
+  app.config.globalProperties.updateConfigByKey = updateConfigByKey;
+  app.config.globalProperties.download = dl;
+  app.config.globalProperties.parseTime = parseTime;
+  app.config.globalProperties.handleTree = handleTree;
+  app.config.globalProperties.addDateRange = addDateRange;
+  app.config.globalProperties.selectDictLabel = selectDictLabel;
+  app.config.globalProperties.selectDictLabels = selectDictLabels;
+  app.config.globalProperties.animate = animate;
 }
diff --git a/src/plugins/tab.ts b/src/plugins/tab.ts
index 97442ff..dd240cd 100644
--- a/src/plugins/tab.ts
+++ b/src/plugins/tab.ts
@@ -1,19 +1,29 @@
-import { useTagsViewStore } from '@/store/modules/tagsView';
 import router from '@/router';
-import { TagView, RouteLocationRaw } from 'vue-router';
+import { RouteLocationMatched, RouteLocationNormalized } from 'vue-router';
+import useTagsViewStore from '@/store/modules/tagsView';
 
 export default {
   /**
    * 鍒锋柊褰撳墠tab椤电
    * @param obj 鏍囩瀵硅薄
    */
-  async refreshPage(obj?: TagView): Promise<void> {
+  async refreshPage(obj?: RouteLocationNormalized): Promise<void> {
     const { path, query, matched } = router.currentRoute.value;
     if (obj === undefined) {
-      matched.forEach((m) => {
+      matched.forEach((m: RouteLocationMatched) => {
         if (m.components && m.components.default && m.components.default.name) {
           if (!['Layout', 'ParentView'].includes(m.components.default.name)) {
-            obj = { name: m.components.default.name, path: path, query: query };
+            obj = {
+              name: m.components.default.name,
+              path: path,
+              query: query,
+              matched: undefined,
+              fullPath: undefined,
+              hash: undefined,
+              params: undefined,
+              redirectedFrom: undefined,
+              meta: undefined
+            };
           }
         }
       });
@@ -31,17 +41,17 @@
     });
   },
   // 鍏抽棴褰撳墠tab椤电锛屾墦寮�鏂伴〉绛�
-  closeOpenPage(obj: RouteLocationRaw): void {
+  closeOpenPage(obj: RouteLocationNormalized): void {
     useTagsViewStore().delView(router.currentRoute.value);
     if (obj !== undefined) {
       router.push(obj);
     }
   },
   // 鍏抽棴鎸囧畾tab椤电
-  async closePage(obj?: TagView): Promise<{ visitedViews: TagView[]; cachedViews: string[] } | any> {
+  async closePage(obj?: RouteLocationNormalized): Promise<{ visitedViews: RouteLocationNormalized[]; cachedViews: string[] } | any> {
     if (obj === undefined) {
       // prettier-ignore
-      const { visitedViews } = await useTagsViewStore().delView(router.currentRoute.value) as any
+      const { visitedViews } = await useTagsViewStore().delView(router.currentRoute.value)
       const latestView = visitedViews.slice(-1)[0];
       if (latestView) {
         return router.push(latestView.fullPath);
@@ -55,15 +65,15 @@
     return useTagsViewStore().delAllViews();
   },
   // 鍏抽棴宸︿晶tab椤电
-  closeLeftPage(obj?: TagView) {
+  closeLeftPage(obj?: RouteLocationNormalized) {
     return useTagsViewStore().delLeftTags(obj || router.currentRoute.value);
   },
   // 鍏抽棴鍙充晶tab椤电
-  closeRightPage(obj?: TagView) {
+  closeRightPage(obj?: RouteLocationNormalized) {
     return useTagsViewStore().delRightTags(obj || router.currentRoute.value);
   },
   // 鍏抽棴鍏朵粬tab椤电
-  closeOtherPage(obj?: TagView) {
+  closeOtherPage(obj?: RouteLocationNormalized) {
     return useTagsViewStore().delOthersViews(obj || router.currentRoute.value);
   },
   /**
@@ -80,7 +90,7 @@
    * 淇敼tab椤电
    * @param obj 鏍囩瀵硅薄
    */
-  updatePage(obj: TagView) {
+  updatePage(obj: RouteLocationNormalized) {
     return useTagsViewStore().updateVisitedView(obj);
   }
 };
diff --git a/src/router/index.ts b/src/router/index.ts
index 271385c..2343685 100644
--- a/src/router/index.ts
+++ b/src/router/index.ts
@@ -1,4 +1,4 @@
-import { createWebHistory, createRouter, RouteOption } from 'vue-router';
+import { createWebHistory, createRouter, RouteRecordRaw } from 'vue-router';
 /* Layout */
 import Layout from '@/layout/index.vue';
 
@@ -25,7 +25,7 @@
  */
 
 // 鍏叡璺敱
-export const constantRoutes: RouteOption[] = [
+export const constantRoutes: RouteRecordRaw[] = [
   {
     path: '/redirect',
     component: Layout,
@@ -92,7 +92,7 @@
 ];
 
 // 鍔ㄦ�佽矾鐢憋紝鍩轰簬鐢ㄦ埛鏉冮檺鍔ㄦ�佸幓鍔犺浇
-export const dynamicRoutes: RouteOption[] = [
+export const dynamicRoutes: RouteRecordRaw[] = [
   {
     path: '/system/user-auth',
     component: Layout,
@@ -162,6 +162,20 @@
         meta: { title: '淇敼鐢熸垚閰嶇疆', activeMenu: '/tool/gen', icon: '' }
       }
     ]
+  },
+  {
+    path: '/demo/leaveEdit',
+    component: Layout,
+    hidden: true,
+    permissions: ['demo:leave:edit'],
+    children: [
+      {
+        path: 'index',
+        component: () => import('@/views/workflow/leave/leaveEdit.vue'),
+        name: 'leaveEdit',
+        meta: { title: '璇峰亣鐢宠', activeMenu: '/demo/leave', noCache: true }
+      }
+    ]
   }
 ];
 
diff --git a/src/settings.ts b/src/settings.ts
index 9cebde7..4b62c75 100644
--- a/src/settings.ts
+++ b/src/settings.ts
@@ -1,3 +1,5 @@
+import { LanguageEnum } from '@/enums/LanguageEnum';
+
 const setting: DefaultSettings = {
   /**
    * 缃戦〉鏍囬
@@ -50,6 +52,11 @@
 
   animationEnable: false,
 
-  dark: false
+  dark: false,
+  language: LanguageEnum.zh_CN,
+
+  size: 'default',
+
+  layout: ''
 };
 export default setting;
diff --git a/src/store/modules/app.ts b/src/store/modules/app.ts
index 90866c1..0205fab 100644
--- a/src/store/modules/app.ts
+++ b/src/store/modules/app.ts
@@ -9,7 +9,7 @@
     hide: false
   });
   const device = ref<string>('desktop');
-  const size = useStorage('size', 'default');
+  const size = useStorage<'large' | 'default' | 'small'>('size', 'default');
 
   // 璇█
   const language = useStorage('language', 'zh_CN');
@@ -43,7 +43,7 @@
   const toggleDevice = (d: string): void => {
     device.value = d;
   };
-  const setSize = (s: string): void => {
+  const setSize = (s: 'large' | 'default' | 'small'): void => {
     size.value = s;
   };
   const toggleSideBarHide = (status: boolean): void => {
diff --git a/src/store/modules/modeler.ts b/src/store/modules/modeler.ts
new file mode 100644
index 0000000..7b19c1a
--- /dev/null
+++ b/src/store/modules/modeler.ts
@@ -0,0 +1,76 @@
+import { Modeler, Modeling, Canvas, ElementRegistry, Moddle, BpmnFactory } from 'bpmn';
+
+type ModelerStore = {
+  modeler: Modeler | undefined;
+  moddle: Moddle | undefined;
+  modeling: Modeling | undefined;
+  canvas: Canvas | undefined;
+  elementRegistry: ElementRegistry | undefined;
+  bpmnFactory: BpmnFactory | undefined;
+  // 娴佺▼瀹氫箟鏍硅妭鐐逛俊鎭�
+  procDefId: string | undefined;
+  procDefName: string | undefined;
+};
+
+const defaultState: ModelerStore = {
+  modeler: undefined,
+  moddle: undefined,
+  modeling: undefined,
+  canvas: undefined,
+  elementRegistry: undefined,
+  bpmnFactory: undefined,
+  procDefId: undefined,
+  procDefName: undefined
+};
+export const useModelerStore = defineStore('modeler', () => {
+  let modeler = defaultState.modeler;
+  let moddle = defaultState.moddle;
+  let modeling = defaultState.modeling;
+  let canvas = defaultState.canvas;
+  let elementRegistry = defaultState.elementRegistry;
+  let bpmnFactory = defaultState.bpmnFactory;
+  const procDefId = ref(defaultState.procDefId);
+  const procDefName = ref(defaultState.procDefName);
+
+  const getModeler = () => modeler;
+  const getModdle = () => moddle;
+  const getModeling = (): Modeling | undefined => modeling;
+  const getCanvas = (): Canvas | undefined => canvas;
+  const getElRegistry = (): ElementRegistry | undefined => elementRegistry;
+  const getBpmnFactory = (): BpmnFactory | undefined => bpmnFactory;
+  const getProcDefId = (): string | undefined => procDefId.value;
+  const getProcDefName = (): string | undefined => procDefName.value;
+
+  // 璁剧疆鏍硅妭鐐�
+  const setModeler = (modelers: Modeler | undefined) => {
+    if (modelers) {
+      modeler = modelers;
+      modeling = modelers.get<Modeling>('modeling');
+      moddle = modelers.get<Moddle>('moddle');
+      canvas = modelers.get<Canvas>('canvas');
+      bpmnFactory = modelers.get<BpmnFactory>('bpmnFactory');
+      elementRegistry = modelers.get<ElementRegistry>('elementRegistry');
+    } else {
+      modeling = moddle = canvas = elementRegistry = bpmnFactory = undefined;
+    }
+  };
+  // 璁剧疆娴佺▼瀹氫箟鏍硅妭鐐逛俊鎭�
+  const setProcDef = (modeler: Modeler | undefined) => {
+    procDefId.value = modeler.get<Canvas>('canvas').getRootElement().businessObject.get('id');
+    procDefName.value = modeler.get<Canvas>('canvas').getRootElement().businessObject.get('name');
+  };
+
+  return {
+    getModeler,
+    getModdle,
+    getModeling,
+    getCanvas,
+    getElRegistry,
+    getBpmnFactory,
+    getProcDefId,
+    getProcDefName,
+    setModeler,
+    setProcDef
+  };
+});
+export default useModelerStore;
diff --git a/src/store/modules/notice.ts b/src/store/modules/notice.ts
index f3f8e5a..de980b6 100644
--- a/src/store/modules/notice.ts
+++ b/src/store/modules/notice.ts
@@ -22,7 +22,7 @@
 
   //瀹炵幇鍏ㄩ儴宸茶
   const readAll = () => {
-    state.notices.forEach((item) => {
+    state.notices.forEach((item: any) => {
       item.read = true;
     });
   };
diff --git a/src/store/modules/permission.ts b/src/store/modules/permission.ts
index ed64e46..cf12a73 100644
--- a/src/store/modules/permission.ts
+++ b/src/store/modules/permission.ts
@@ -2,35 +2,46 @@
 import router, { constantRoutes, dynamicRoutes } from '@/router';
 import store from '@/store';
 import { getRouters } from '@/api/menu';
+import auth from '@/plugins/auth';
+import { RouteRecordRaw } from 'vue-router';
+
 import Layout from '@/layout/index.vue';
 import ParentView from '@/components/ParentView/index.vue';
 import InnerLink from '@/layout/components/InnerLink/index.vue';
-import auth from '@/plugins/auth';
-import { RouteOption } from 'vue-router';
+
 // 鍖归厤views閲岄潰鎵�鏈夌殑.vue鏂囦欢
 const modules = import.meta.glob('./../../views/**/*.vue');
-
 export const usePermissionStore = defineStore('permission', () => {
-  const routes = ref<RouteOption[]>([]);
-  const addRoutes = ref<RouteOption[]>([]);
-  const defaultRoutes = ref<RouteOption[]>([]);
-  const topbarRouters = ref<RouteOption[]>([]);
-  const sidebarRouters = ref<RouteOption[]>([]);
+  const routes = ref<RouteRecordRaw[]>([]);
+  const addRoutes = ref<RouteRecordRaw[]>([]);
+  const defaultRoutes = ref<RouteRecordRaw[]>([]);
+  const topbarRouters = ref<RouteRecordRaw[]>([]);
+  const sidebarRouters = ref<RouteRecordRaw[]>([]);
 
-  const setRoutes = (newRoutes: RouteOption[]): void => {
+  const getRoutes = (): RouteRecordRaw[] => {
+    return routes.value;
+  };
+  const getSidebarRoutes = (): RouteRecordRaw[] => {
+    return sidebarRouters.value;
+  };
+  const getTopbarRoutes = (): RouteRecordRaw[] => {
+    return topbarRouters.value;
+  };
+
+  const setRoutes = (newRoutes: RouteRecordRaw[]): void => {
     addRoutes.value = newRoutes;
     routes.value = constantRoutes.concat(newRoutes);
   };
-  const setDefaultRoutes = (routes: RouteOption[]): void => {
+  const setDefaultRoutes = (routes: RouteRecordRaw[]): void => {
     defaultRoutes.value = constantRoutes.concat(routes);
   };
-  const setTopbarRoutes = (routes: RouteOption[]): void => {
+  const setTopbarRoutes = (routes: RouteRecordRaw[]): void => {
     topbarRouters.value = routes;
   };
-  const setSidebarRouters = (routes: RouteOption[]): void => {
+  const setSidebarRouters = (routes: RouteRecordRaw[]): void => {
     sidebarRouters.value = routes;
   };
-  const generateRoutes = async (): Promise<RouteOption[]> => {
+  const generateRoutes = async (): Promise<RouteRecordRaw[]> => {
     const res = await getRouters();
     const { data } = res;
     const sdata = JSON.parse(JSON.stringify(data));
@@ -47,7 +58,7 @@
     setSidebarRouters(constantRoutes.concat(sidebarRoutes));
     setDefaultRoutes(sidebarRoutes);
     setTopbarRoutes(defaultRoutes);
-    return new Promise<RouteOption[]>((resolve) => resolve(rewriteRoutes));
+    return new Promise<RouteRecordRaw[]>((resolve) => resolve(rewriteRoutes));
   };
 
   /**
@@ -56,22 +67,20 @@
    * @param lastRouter 涓婁竴绾ц矾鐢�
    * @param type 鏄惁鏄噸鍐欒矾鐢�
    */
-  const filterAsyncRouter = (asyncRouterMap: RouteOption[], lastRouter?: RouteOption, type = false): RouteOption[] => {
+  const filterAsyncRouter = (asyncRouterMap: RouteRecordRaw[], lastRouter?: RouteRecordRaw, type = false): RouteRecordRaw[] => {
     return asyncRouterMap.filter((route) => {
       if (type && route.children) {
         route.children = filterChildren(route.children, undefined);
       }
-      if (route.component) {
-        // Layout ParentView 缁勪欢鐗规畩澶勭悊
-        if (route.component === 'Layout') {
-          route.component = Layout;
-        } else if (route.component === 'ParentView') {
-          route.component = ParentView;
-        } else if (route.component === 'InnerLink') {
-          route.component = InnerLink;
-        } else {
-          route.component = loadView(route.component);
-        }
+      // Layout ParentView 缁勪欢鐗规畩澶勭悊
+      if (route.component?.toString() === 'Layout') {
+        route.component = Layout;
+      } else if (route.component?.toString() === 'ParentView') {
+        route.component = ParentView;
+      } else if (route.component?.toString() === 'InnerLink') {
+        route.component = InnerLink;
+      } else {
+        route.component = loadView(route.component);
       }
       if (route.children != null && route.children && route.children.length) {
         route.children = filterAsyncRouter(route.children, route, type);
@@ -82,11 +91,11 @@
       return true;
     });
   };
-  const filterChildren = (childrenMap: RouteOption[], lastRouter?: RouteOption): RouteOption[] => {
-    let children: RouteOption[] = [];
+  const filterChildren = (childrenMap: RouteRecordRaw[], lastRouter?: RouteRecordRaw): RouteRecordRaw[] => {
+    let children: RouteRecordRaw[] = [];
     childrenMap.forEach((el) => {
       if (el.children && el.children.length) {
-        if (el.component === 'ParentView' && !lastRouter) {
+        if (el.component?.toString() === 'ParentView' && !lastRouter) {
           el.children.forEach((c) => {
             c.path = el.path + '/' + c.path;
             if (c.children && c.children.length) {
@@ -101,20 +110,33 @@
       if (lastRouter) {
         el.path = lastRouter.path + '/' + el.path;
         if (el.children && el.children.length) {
-          children = children.concat(filterChildren(el.children, el))
-          return
+          children = children.concat(filterChildren(el.children, el));
+          return;
         }
       }
       children = children.concat(el);
     });
     return children;
   };
-  return { routes, setRoutes, generateRoutes, setSidebarRouters, topbarRouters, sidebarRouters, defaultRoutes };
+  return {
+    routes,
+    topbarRouters,
+    sidebarRouters,
+    defaultRoutes,
+
+    getRoutes,
+    getSidebarRoutes,
+    getTopbarRoutes,
+
+    setRoutes,
+    generateRoutes,
+    setSidebarRouters
+  };
 });
 
 // 鍔ㄦ�佽矾鐢遍亶鍘嗭紝楠岃瘉鏄惁鍏峰鏉冮檺
-export const filterDynamicRoutes = (routes: RouteOption[]) => {
-  const res: RouteOption[] = [];
+export const filterDynamicRoutes = (routes: RouteRecordRaw[]) => {
+  const res: RouteRecordRaw[] = [];
   routes.forEach((route) => {
     if (route.permissions) {
       if (auth.hasPermiOr(route.permissions)) {
diff --git a/src/store/modules/settings.ts b/src/store/modules/settings.ts
index fcf595c..ef49b3c 100644
--- a/src/store/modules/settings.ts
+++ b/src/store/modules/settings.ts
@@ -1,35 +1,29 @@
 import { defineStore } from 'pinia';
 import defaultSettings from '@/settings';
-import { SettingTypeEnum } from '@/enums/SettingTypeEnum';
 import { useDynamicTitle } from '@/utils/dynamicTitle';
-import { Ref } from 'vue';
 
 export const useSettingsStore = defineStore('setting', () => {
-  const storageSetting = JSON.parse(localStorage.getItem('layout-setting') || '{}');
+  const storageSetting = useStorage<LayoutSetting>('layout-setting', {
+    topNav: defaultSettings.topNav,
+    tagsView: defaultSettings.tagsView,
+    fixedHeader: defaultSettings.fixedHeader,
+    sidebarLogo: defaultSettings.sidebarLogo,
+    dynamicTitle: defaultSettings.dynamicTitle,
+    sideTheme: defaultSettings.sideTheme,
+    theme: defaultSettings.theme
+  });
+  const title = ref<string>(defaultSettings.title);
+  const theme = ref<string>(storageSetting.value.theme);
+  const sideTheme = ref<string>(storageSetting.value.sideTheme);
+  const showSettings = ref<boolean>(defaultSettings.showSettings);
+  const topNav = ref<boolean>(storageSetting.value.topNav);
+  const tagsView = ref<boolean>(storageSetting.value.tagsView);
+  const fixedHeader = ref<boolean>(storageSetting.value.fixedHeader);
+  const sidebarLogo = ref<boolean>(storageSetting.value.sidebarLogo);
+  const dynamicTitle = ref<boolean>(storageSetting.value.dynamicTitle);
+  const animationEnable = ref<boolean>(defaultSettings.animationEnable);
+  const dark = ref<boolean>(defaultSettings.dark);
 
-  const prop: { [key: string]: Ref<any> } = {
-    title: ref<string>(''),
-    theme: ref<string>(storageSetting.theme || defaultSettings.theme),
-    sideTheme: ref<string>(storageSetting.sideTheme || defaultSettings.sideTheme),
-    showSettings: ref<boolean>(storageSetting.showSettings || defaultSettings.showSettings),
-    topNav: ref<boolean>(storageSetting.topNav === undefined ? defaultSettings.topNav : storageSetting.topNav),
-    tagsView: ref<boolean>(storageSetting.tagsView === undefined ? defaultSettings.tagsView : storageSetting.tagsView),
-    fixedHeader: ref<boolean>(storageSetting.fixedHeader === undefined ? defaultSettings.fixedHeader : storageSetting.fixedHeader),
-    sidebarLogo: ref<boolean>(storageSetting.sidebarLogo === undefined ? defaultSettings.sidebarLogo : storageSetting.sidebarLogo),
-    dynamicTitle: ref<boolean>(storageSetting.dynamicTitle === undefined ? defaultSettings.dynamicTitle : storageSetting.dynamicTitle),
-    animationEnable: ref<boolean>(storageSetting.animationEnable === undefined ? defaultSettings.animationEnable : storageSetting.animationEnable),
-    dark: ref<boolean>(storageSetting.dark || defaultSettings.dark)
-  };
-
-  const { title, theme, sideTheme, showSettings, topNav, tagsView, fixedHeader, sidebarLogo, dynamicTitle, animationEnable, dark } = prop;
-
-  // actions
-  const changeSetting = (param: { key: SettingTypeEnum; value: any }) => {
-    const { key, value } = param;
-    if (key in prop) {
-      prop[key].value = value;
-    }
-  };
   const setTitle = (value: string) => {
     title.value = value;
     useDynamicTitle();
@@ -46,7 +40,6 @@
     dynamicTitle,
     animationEnable,
     dark,
-    changeSetting,
     setTitle
   };
 });
diff --git a/src/store/modules/tagsView.ts b/src/store/modules/tagsView.ts
index 1c1c9a2..b9502eb 100644
--- a/src/store/modules/tagsView.ts
+++ b/src/store/modules/tagsView.ts
@@ -1,38 +1,53 @@
-import { TagView, RouteRecordNormalized } from 'vue-router';
+import { RouteLocationNormalized } from 'vue-router';
 
 export const useTagsViewStore = defineStore('tagsView', () => {
-  const visitedViews = ref<TagView[]>([]);
+  const visitedViews = ref<RouteLocationNormalized[]>([]);
   const cachedViews = ref<string[]>([]);
-  const iframeViews = ref<TagView[]>([]);
+  const iframeViews = ref<RouteLocationNormalized[]>([]);
 
-  const addView = (view: TagView) => {
+  const getVisitedViews = (): RouteLocationNormalized[] => {
+    return visitedViews.value;
+  };
+  const getIframeViews = (): RouteLocationNormalized[] => {
+    return iframeViews.value;
+  };
+  const getCachedViews = (): string[] => {
+    return cachedViews.value;
+  };
+
+  const addView = (view: RouteLocationNormalized) => {
     addVisitedView(view);
     addCachedView(view);
   };
 
-  const addIframeView = (view: TagView): void => {
-    if (iframeViews.value.some((v) => v.path === view.path)) return;
+  const addIframeView = (view: RouteLocationNormalized): void => {
+    if (iframeViews.value.some((v: RouteLocationNormalized) => v.path === view.path)) return;
     iframeViews.value.push(
       Object.assign({}, view, {
         title: view.meta?.title || 'no-name'
       })
     );
   };
-  const delIframeView = (view: TagView): Promise<TagView[]> => {
+  const delIframeView = (view: RouteLocationNormalized): Promise<RouteLocationNormalized[]> => {
     return new Promise((resolve) => {
-      iframeViews.value = iframeViews.value.filter((item) => item.path !== view.path);
+      iframeViews.value = iframeViews.value.filter((item: RouteLocationNormalized) => item.path !== view.path);
       resolve([...iframeViews.value]);
     });
   };
-  const addVisitedView = (view: TagView): void => {
-    if (visitedViews.value.some((v) => v.path === view.path)) return;
+  const addVisitedView = (view: RouteLocationNormalized): void => {
+    if (visitedViews.value.some((v: RouteLocationNormalized) => v.path === view.path)) return;
     visitedViews.value.push(
       Object.assign({}, view, {
         title: view.meta?.title || 'no-name'
       })
     );
   };
-  const delView = (view: TagView): Promise<{ visitedViews: TagView[]; cachedViews: string[] }> => {
+  const delView = (
+    view: RouteLocationNormalized
+  ): Promise<{
+    visitedViews: RouteLocationNormalized[];
+    cachedViews: string[];
+  }> => {
     return new Promise((resolve) => {
       delVisitedView(view);
       if (!isDynamicRoute(view)) {
@@ -45,7 +60,7 @@
     });
   };
 
-  const delVisitedView = (view: TagView): Promise<TagView[]> => {
+  const delVisitedView = (view: RouteLocationNormalized): Promise<RouteLocationNormalized[]> => {
     return new Promise((resolve) => {
       for (const [i, v] of visitedViews.value.entries()) {
         if (v.path === view.path) {
@@ -56,7 +71,7 @@
       resolve([...visitedViews.value]);
     });
   };
-  const delCachedView = (view?: TagView): Promise<string[]> => {
+  const delCachedView = (view?: RouteLocationNormalized): Promise<string[]> => {
     let viewName = '';
     if (view) {
       viewName = view.name as string;
@@ -67,7 +82,12 @@
       resolve([...cachedViews.value]);
     });
   };
-  const delOthersViews = (view: TagView): Promise<{ visitedViews: TagView[]; cachedViews: string[] }> => {
+  const delOthersViews = (
+    view: RouteLocationNormalized
+  ): Promise<{
+    visitedViews: RouteLocationNormalized[];
+    cachedViews: string[];
+  }> => {
     return new Promise((resolve) => {
       delOthersVisitedViews(view);
       delOthersCachedViews(view);
@@ -78,15 +98,15 @@
     });
   };
 
-  const delOthersVisitedViews = (view: TagView): Promise<TagView[]> => {
+  const delOthersVisitedViews = (view: RouteLocationNormalized): Promise<RouteLocationNormalized[]> => {
     return new Promise((resolve) => {
-      visitedViews.value = visitedViews.value.filter((v) => {
+      visitedViews.value = visitedViews.value.filter((v: RouteLocationNormalized) => {
         return v.meta?.affix || v.path === view.path;
       });
       resolve([...visitedViews.value]);
     });
   };
-  const delOthersCachedViews = (view: TagView): Promise<string[]> => {
+  const delOthersCachedViews = (view: RouteLocationNormalized): Promise<string[]> => {
     const viewName = view.name as string;
     return new Promise((resolve) => {
       const index = cachedViews.value.indexOf(viewName);
@@ -99,7 +119,7 @@
     });
   };
 
-  const delAllViews = (): Promise<{ visitedViews: TagView[]; cachedViews: string[] }> => {
+  const delAllViews = (): Promise<{ visitedViews: RouteLocationNormalized[]; cachedViews: string[] }> => {
     return new Promise((resolve) => {
       delAllVisitedViews();
       delAllCachedViews();
@@ -109,9 +129,9 @@
       });
     });
   };
-  const delAllVisitedViews = (): Promise<TagView[]> => {
+  const delAllVisitedViews = (): Promise<RouteLocationNormalized[]> => {
     return new Promise((resolve) => {
-      visitedViews.value = visitedViews.value.filter((tag) => tag.meta?.affix);
+      visitedViews.value = visitedViews.value.filter((tag: RouteLocationNormalized) => tag.meta?.affix);
       resolve([...visitedViews.value]);
     });
   };
@@ -123,7 +143,7 @@
     });
   };
 
-  const updateVisitedView = (view: TagView): void => {
+  const updateVisitedView = (view: RouteLocationNormalized): void => {
     for (let v of visitedViews.value) {
       if (v.path === view.path) {
         v = Object.assign(v, view);
@@ -131,13 +151,13 @@
       }
     }
   };
-  const delRightTags = (view: TagView): Promise<TagView[]> => {
+  const delRightTags = (view: RouteLocationNormalized): Promise<RouteLocationNormalized[]> => {
     return new Promise((resolve) => {
-      const index = visitedViews.value.findIndex((v) => v.path === view.path);
+      const index = visitedViews.value.findIndex((v: RouteLocationNormalized) => v.path === view.path);
       if (index === -1) {
         return;
       }
-      visitedViews.value = visitedViews.value.filter((item, idx) => {
+      visitedViews.value = visitedViews.value.filter((item: RouteLocationNormalized, idx: number) => {
         if (idx <= index || (item.meta && item.meta.affix)) {
           return true;
         }
@@ -150,13 +170,13 @@
       resolve([...visitedViews.value]);
     });
   };
-  const delLeftTags = (view: TagView): Promise<TagView[]> => {
+  const delLeftTags = (view: RouteLocationNormalized): Promise<RouteLocationNormalized[]> => {
     return new Promise((resolve) => {
-      const index = visitedViews.value.findIndex((v) => v.path === view.path);
+      const index = visitedViews.value.findIndex((v: RouteLocationNormalized) => v.path === view.path);
       if (index === -1) {
         return;
       }
-      visitedViews.value = visitedViews.value.filter((item, idx) => {
+      visitedViews.value = visitedViews.value.filter((item: RouteLocationNormalized, idx: number) => {
         if (idx >= index || (item.meta && item.meta.affix)) {
           return true;
         }
@@ -170,7 +190,7 @@
     });
   };
 
-  const addCachedView = (view: TagView): void => {
+  const addCachedView = (view: RouteLocationNormalized): void => {
     const viewName = view.name as string;
     if (!viewName) return;
     if (cachedViews.value.includes(viewName)) return;
@@ -179,15 +199,20 @@
     }
   };
 
-  const isDynamicRoute = (view: any): boolean => {
+  const isDynamicRoute = (view: RouteLocationNormalized): boolean => {
     // 妫�鏌ュ尮閰嶇殑璺敱璁板綍涓槸鍚︽湁鍔ㄦ�佹
-    return view.matched.some((m: RouteRecordNormalized) => m.path.includes(':'));
+    return view.matched.some((m) => m.path.includes(':'));
   };
 
   return {
     visitedViews,
     cachedViews,
     iframeViews,
+
+    getVisitedViews,
+    getIframeViews,
+    getCachedViews,
+
     addVisitedView,
     addCachedView,
     delVisitedView,
@@ -205,5 +230,4 @@
     delIframeView
   };
 });
-
 export default useTagsViewStore;
diff --git a/src/store/modules/user.ts b/src/store/modules/user.ts
index 7c02de0..4122294 100644
--- a/src/store/modules/user.ts
+++ b/src/store/modules/user.ts
@@ -1,9 +1,9 @@
 import { to } from 'await-to-js';
-import defAva from '@/assets/images/profile.jpg';
-import store from '@/store';
 import { getToken, removeToken, setToken } from '@/utils/auth';
 import { login as loginApi, logout as logoutApi, getInfo as getUserInfo } from '@/api/login';
 import { LoginData } from '@/api/types';
+import defAva from '@/assets/images/profile.jpg';
+import store from '@/store';
 
 export const useUserStore = defineStore('user', () => {
   const token = ref(getToken());
diff --git a/src/types/axios.d.ts b/src/types/axios.d.ts
index 36a9f8f..9f2c6d2 100644
--- a/src/types/axios.d.ts
+++ b/src/types/axios.d.ts
@@ -1,7 +1,6 @@
-import axios from 'axios';
-
+export {};
 declare module 'axios' {
-  export interface AxiosResponse<T = any> {
+  interface AxiosResponse<T = any> {
     code: number;
     msg: string;
     rows: T;
diff --git a/src/types/bpmn/editor/global.d.ts b/src/types/bpmn/editor/global.d.ts
new file mode 100644
index 0000000..1b9a634
--- /dev/null
+++ b/src/types/bpmn/editor/global.d.ts
@@ -0,0 +1,13 @@
+import { MessageApiInjection } from 'naive-ui/lib/message/src/MessageProvider';
+
+declare global {
+  interface Window {
+    bpmnInstances: any;
+    __messageBox: MessageApiInjection;
+    URL: any;
+  }
+}
+
+declare interface Window {
+  bpmnInstances: any;
+}
diff --git a/src/types/bpmn/index.d.ts b/src/types/bpmn/index.d.ts
new file mode 100644
index 0000000..f8e8d15
--- /dev/null
+++ b/src/types/bpmn/index.d.ts
@@ -0,0 +1,15 @@
+declare module 'bpmn' {
+  import type modeler from 'bpmn-js/lib/Modeler';
+  import type modeling from 'bpmn-js/lib/features/modeling/Modeling';
+  import type canvas from 'diagram-js/lib/core/Canvas';
+  import type elementRegistry from 'diagram-js/lib/core/ElementRegistry';
+  import type bpmnFactory from 'bpmn-js/lib/features/modeling/BpmnFactory';
+
+  export type Modeler = modeler;
+  export type Modeling = modeling;
+  export type Canvas = canvas;
+  export type ElementRegistry = elementRegistry;
+  export type Moddle = import('moddle').Moddle;
+  export type ModdleElement = import('moddle').ModdleElement;
+  export type BpmnFactory = bpmnFactory;
+}
diff --git a/src/types/bpmn/moddle.d.ts b/src/types/bpmn/moddle.d.ts
new file mode 100644
index 0000000..1ed7933
--- /dev/null
+++ b/src/types/bpmn/moddle.d.ts
@@ -0,0 +1,37 @@
+declare module 'moddle' {
+  import type { Element as element } from 'bpmn-js/lib/model/Types';
+
+  export type Element = {
+    get<T>(name: string): T;
+
+    set(name: string, value: any): void;
+  } & element;
+
+  export interface ModdleElement extends Element {
+    $model: Moddle;
+    readonly $type: string;
+    $attrs: object | {};
+    $parent: any;
+    businessObject: ModdleElement;
+    type: string;
+
+    [field: string]: any;
+
+    hasType(element: ModdleElement, type?: string): boolean;
+  }
+
+  export interface Package {
+    name: string;
+    prefix: string;
+  }
+
+  export interface Moddle {
+    typeCache: Record<string, ModdleElement>;
+
+    getPackage: typeof Registry.prototype.getPackage;
+
+    getPackages: typeof Registry.prototype.getPackages;
+
+    create(type: string, attrs?: any): ModdleElement;
+  }
+}
diff --git a/src/types/bpmn/panel.d.ts b/src/types/bpmn/panel.d.ts
new file mode 100644
index 0000000..3179261
--- /dev/null
+++ b/src/types/bpmn/panel.d.ts
@@ -0,0 +1,92 @@
+declare module 'bpmnDesign' {
+  import { AllocationTypeEnum, SpecifyDescEnum, MultiInstanceTypeEnum } from '@/enums/bpmn/IndexEnums';
+
+  export interface ParamVO {
+    type: string;
+    name: string;
+    value: string;
+  }
+
+  export interface TaskListenerVO {
+    event: string;
+    type: string;
+    name: string;
+    className: string;
+    params: ParamVO[];
+  }
+
+  export interface ExecutionListenerVO {
+    event: string;
+    type: string;
+    className: string;
+    params: ParamVO[];
+  }
+
+  interface BasePanel {
+    id: string;
+    name: string;
+  }
+  export interface ProcessPanel extends BasePanel {}
+
+  export interface TaskPanel extends BasePanel {
+    allocationType: AllocationTypeEnum;
+    specifyDesc: SpecifyDescEnum;
+    multiInstanceType: MultiInstanceTypeEnum;
+    async?: boolean;
+    priority?: number;
+    formKey?: string;
+    skipExpression?: string;
+    isForCompensation?: boolean;
+    triggerServiceTask?: boolean;
+    autoStoreVariables?: boolean;
+    ruleVariablesInput?: string;
+    excludeTaskListener?: boolean;
+    exclude?: boolean;
+    class?: string;
+    dueDate?: string;
+    fixedAssignee?: string;
+
+    candidateUsers?: string;
+    assignee?: string;
+    candidateGroups?: string;
+    collection?: string;
+    elementVariable?: string;
+    completionCondition?: string;
+    isSequential?: boolean;
+
+    loopCharacteristics?: {
+      collection: string;
+      elementVariable: string;
+      isSequential: boolean;
+      completionCondition: {
+        body: string;
+      };
+    };
+  }
+
+  export interface StartEndPanel extends BasePanel {}
+  export interface GatewayPanel extends BasePanel {}
+  export interface SequenceFlowPanel extends BasePanel {
+    conditionExpression: {
+      body: string;
+    };
+    conditionExpressionValue: string;
+    skipExpression: string;
+  }
+
+  export interface ParticipantPanel extends BasePanel {}
+  export interface SubProcessPanel extends BasePanel {
+    multiInstanceType: MultiInstanceTypeEnum;
+    collection?: string;
+    elementVariable?: string;
+    completionCondition?: string;
+    loopCharacteristics?: {
+      collection: string;
+      elementVariable: string;
+      isSequential: boolean;
+      completionCondition: {
+        body: string;
+      };
+    };
+  }
+}
diff --git a/src/types/element.d.ts b/src/types/element.d.ts
index ba7598d..ae6ecfc 100644
--- a/src/types/element.d.ts
+++ b/src/types/element.d.ts
@@ -1,34 +1,35 @@
 import type * as ep from 'element-plus';
 declare global {
-  declare type ElTagType = '' | 'success' | 'warning' | 'info' | 'danger' | 'default' | 'primary';
-  declare type ElFormInstance = InstanceType<typeof ep.ElForm>;
-  declare type ElTableInstance = InstanceType<typeof ep.ElTable>;
+  declare type ElTagType = 'primary' | 'success' | 'info' | 'warning' | 'danger';
+  declare type ElFormInstance = ep.FormInstance;
+  declare type ElTableInstance = ep.TableInstance;
+  declare type ElUploadInstance = ep.UploadInstance;
+  declare type ElScrollbarInstance = ep.ScrollbarInstance;
+  declare type ElInputInstance = ep.InputInstance;
+  declare type ElInputNumberInstance = ep.InputNumberInstance;
+  declare type ElRadioInstance = ep.RadioInstance;
+  declare type ElRadioGroupInstance = ep.RadioGroupInstance;
+  declare type ElRadioButtonInstance = ep.RadioButtonInstance;
+  declare type ElCheckboxInstance = ep.CheckboxInstance;
+  declare type ElSwitchInstance = ep.SwitchInstance;
+  declare type ElCascaderInstance = ep.CascaderInstance;
+  declare type ElColorPickerInstance = ep.ColorPickerInstance;
+  declare type ElRateInstance = ep.RateInstance;
+  declare type ElSliderInstance = ep.SliderInstance;
+
   declare type ElTreeInstance = InstanceType<typeof ep.ElTree>;
   declare type ElTreeSelectInstance = InstanceType<typeof ep.ElTreeSelect>;
   declare type ElSelectInstance = InstanceType<typeof ep.ElSelect>;
-  declare type ElUploadInstance = InstanceType<typeof ep.ElUpload>;
   declare type ElCardInstance = InstanceType<typeof ep.ElCard>;
   declare type ElDialogInstance = InstanceType<typeof ep.ElDialog>;
-  declare type ElInputInstance = InstanceType<typeof ep.ElInput>;
-  declare type ElInputNumberInstance = InstanceType<typeof ep.ElInputNumber>;
-  declare type ElRadioInstance = InstanceType<typeof ep.ElRadio>;
-  declare type ElRadioGroupInstance = InstanceType<typeof ep.ElRadioGroup>;
-  declare type ElRadioButtonInstance = InstanceType<typeof ep.ElRadioButton>;
-  declare type ElCheckboxInstance = InstanceType<typeof ep.ElCheckbox>;
   declare type ElCheckboxGroupInstance = InstanceType<typeof ep.ElCheckboxGroup>;
-  declare type ElSwitchInstance = InstanceType<typeof ep.ElSwitch>;
   declare type ElDatePickerInstance = InstanceType<typeof ep.ElDatePicker>;
   declare type ElTimePickerInstance = InstanceType<typeof ep.ElTimePicker>;
   declare type ElTimeSelectInstance = InstanceType<typeof ep.ElTimeSelect>;
-  declare type ElCascaderInstance = InstanceType<typeof ep.ElCascader>;
-  declare type ElColorPickerInstance = InstanceType<typeof ep.ElColorPicker>;
-  declare type ElRateInstance = InstanceType<typeof ep.ElRate>;
-  declare type ElSliderInstance = InstanceType<typeof ep.ElSlider>;
-  declare type ElScrollbarInstance = InstanceType<typeof ep.ElScrollbar>;
 
   declare type TransferKey = ep.TransferKey;
   declare type CheckboxValueType = ep.CheckboxValueType;
   declare type ElFormRules = ep.FormRules;
   declare type DateModelType = ep.DateModelType;
-  declare type UploadFile = typeof ep.UploadFile;
+  declare type UploadFile = ep.UploadFile;
 }
diff --git a/src/types/env.d.ts b/src/types/env.d.ts
index 9560307..bfc37cc 100644
--- a/src/types/env.d.ts
+++ b/src/types/env.d.ts
@@ -1,62 +1,9 @@
 declare module '*.vue' {
   import { DefineComponent } from 'vue';
-  const component: DefineComponent<{}, {}, any>;
-  export default component;
-}
-declare module '*.avif' {
-  const src: string;
-  export default src;
+  const Component: DefineComponent<{}, {}, any>;
+  export default Component;
 }
 
-declare module '*.bmp' {
-  const src: string;
-  export default src;
-}
-
-declare module '*.gif' {
-  const src: string;
-  export default src;
-}
-
-declare module '*.jpg' {
-  const src: string;
-  export default src;
-}
-
-declare module '*.jpeg' {
-  const src: string;
-  export default src;
-}
-
-declare module '*.png' {
-  const src: string;
-  export default src;
-}
-
-declare module '*.webp' {
-  const src: string;
-  export default src;
-}
-
-declare module '*.svg' {
-  const src: string;
-  export default src;
-}
-
-declare module '*.module.css' {
-  const classes: { readonly [key: string]: string };
-  export default classes;
-}
-
-declare module '*.module.scss' {
-  const classes: { readonly [key: string]: string };
-  export default classes;
-}
-
-declare module '*.module.sass' {
-  const classes: { readonly [key: string]: string };
-  export default classes;
-}
 // 鐜鍙橀噺
 interface ImportMetaEnv {
   VITE_APP_TITLE: string;
@@ -66,7 +13,9 @@
   VITE_APP_CONTEXT_PATH: string;
   VITE_APP_MONITRO_ADMIN: string;
   VITE_APP_POWERJOB_ADMIN: string;
+  VITE_APP_EASYRETRY_ADMIN: string;
   VITE_APP_ENV: string;
+  VITE_APP_ENCRYPT: string;
   VITE_APP_RSA_PUBLIC_KEY: string;
   VITE_APP_RSA_PRIVATE_KEY: string;
   VITE_APP_CLIENT_ID: string;
diff --git a/src/types/global.d.ts b/src/types/global.d.ts
index 32081bf..17476a3 100644
--- a/src/types/global.d.ts
+++ b/src/types/global.d.ts
@@ -1,4 +1,5 @@
-import type { ComponentInternalInstance as ComponentInstance, PropType as VuePropType } from 'vue';
+import type { PropType as VuePropType, ComponentInternalInstance as ComponentInstance } from 'vue';
+import { LanguageEnum } from '@/enums/LanguageEnum';
 
 declare global {
   /** vue Instance */
@@ -49,6 +50,8 @@
     /** 鏄惁绂佺敤涓婁紶 */
     isUploading: boolean;
 
+    updateSupport: number;
+
     /** 鍏朵粬鍙傛暟 */
     [key: string]: any;
   }
@@ -87,5 +90,77 @@
     pageNum: number;
     pageSize: number;
   }
+  declare interface LayoutSetting {
+    /**
+     * 鏄惁鏄剧ず椤堕儴瀵艰埅
+     */
+    topNav: boolean;
+
+    /**
+     * 鏄惁鏄剧ず澶氭爣绛惧鑸�
+     */
+    tagsView: boolean;
+    /**
+     * 鏄惁鍥哄畾澶撮儴
+     */
+    fixedHeader: boolean;
+    /**
+     * 鏄惁鏄剧ず渚ц竟鏍廘ogo
+     */
+    sidebarLogo: boolean;
+    /**
+     * 鏄惁鏄剧ず鍔ㄦ�佹爣棰�
+     */
+    dynamicTitle: boolean;
+    /**
+     * 渚ц竟鏍忎富棰� theme-dark | theme-light
+     */
+    sideTheme: string;
+    /**
+     * 涓婚妯″紡
+     */
+    theme: string;
+  }
+
+  declare interface DefaultSettings extends LayoutSetting {
+    /**
+     * 缃戦〉鏍囬
+     */
+    title: string;
+
+    /**
+     * 鏄惁鏄剧ず绯荤粺甯冨眬璁剧疆
+     */
+    showSettings: boolean;
+
+    /**
+     * 瀵艰埅鏍忓竷灞�
+     */
+    layout: string;
+
+    /**
+     * 甯冨眬澶у皬
+     */
+    size: 'large' | 'default' | 'small';
+
+    /**
+     * 璇█
+     */
+    language: LanguageEnum;
+
+    /**
+     * 鏄惁鍚敤鍔ㄧ敾鏁堟灉
+     */
+    animationEnable: boolean;
+    /**
+     *  鏄惁鍚敤鏆楅粦妯″紡
+     *
+     * true:鏆楅粦妯″紡
+     * false: 鏄庝寒妯″紡
+     */
+    dark: boolean;
+
+    errorLog: string;
+  }
 }
 export {};
diff --git a/src/types/module.d.ts b/src/types/module.d.ts
index 987c349..984df9f 100644
--- a/src/types/module.d.ts
+++ b/src/types/module.d.ts
@@ -1,15 +1,17 @@
-import type modal from '@/plugins/modal';
-import type tab from '@/plugins/tab';
-import type download from '@/plugins/download';
-import type auth from '@/plugins/auth';
-import type cache from '@/plugins/cache';
-import type animate from '@/animate';
-import type { useDict } from '@/utils/dict';
-import type { addDateRange, handleTree, selectDictLabel, selectDictLabels, parseTime } from '@/utils/ruoyi';
-import type { getConfigKey, updateConfigByKey } from '@/api/system/config';
-import type { download as rd } from '@/utils/request';
+import modal from '@/plugins/modal';
+import tab from '@/plugins/tab';
+import download from '@/plugins/download';
+import auth from '@/plugins/auth';
+import cache from '@/plugins/cache';
+import animate from '@/animate';
+import { useDict } from '@/utils/dict';
+import { handleTree, addDateRange, selectDictLabel, selectDictLabels, parseTime } from '@/utils/ruoyi';
+import { getConfigKey, updateConfigByKey } from '@/api/system/config';
+import { download as rd } from '@/utils/request';
 
-declare module '@vue/runtime-core' {
+export {};
+
+declare module 'vue' {
   interface ComponentCustomProperties {
     // 鍏ㄥ眬鏂规硶澹版槑
     $modal: typeof modal;
@@ -30,3 +32,8 @@
     parseTime: typeof parseTime;
   }
 }
+
+declare module 'vform3-builds' {
+  const content: any;
+  export = content;
+}
diff --git a/src/types/router.d.ts b/src/types/router.d.ts
index 9731fa2..11a60a0 100644
--- a/src/types/router.d.ts
+++ b/src/types/router.d.ts
@@ -1,36 +1,38 @@
-import { RouteRecordRaw } from 'vue-router';
-
+import { LocationQuery, type RouteMeta as VRouteMeta } from 'vue-router';
 declare module 'vue-router' {
-  declare type RouteOption = {
-    hidden?: boolean;
+  interface RouteMeta extends VRouteMeta {
+    link?: string;
+    title?: string;
+    affix?: boolean;
+    noCache?: boolean;
+    activeMenu?: string;
+    icon?: string;
+    breadcrumb?: boolean;
+  }
+
+  interface _RouteRecordBase {
+    hidden?: boolean | string | number;
     permissions?: string[];
     roles?: string[];
-    component?: any;
-    children?: RouteOption[];
     alwaysShow?: boolean;
-    parentPath?: string;
-    meta?: {
-      title: string;
-      icon: string;
-    };
     query?: string;
-  } & RouteRecordRaw;
-
-  declare interface _RouteLocationBase {
-    children?: RouteOption[];
+    parentPath?: string;
   }
 
-  declare interface RouteLocationOptions {
-    fullPath?: string;
-  }
-
-  declare interface TagView extends Partial<_RouteLocationBase> {
+  interface _RouteLocationBase {
+    children?: _RouteRecordBase[];
+    path?: string;
     title?: string;
-    meta?: {
-      link?: string;
-      title?: string;
-      affix?: boolean;
-      noCache?: boolean;
-    };
+  }
+
+  interface TagView {
+    fullPath?: string;
+    name?: string;
+    path?: string;
+    title?: string;
+    meta?: RouteMeta;
+    query?: LocationQuery;
   }
 }
+
+export {};
diff --git a/src/types/setting.d.ts b/src/types/setting.d.ts
deleted file mode 100644
index 1b5f9fd..0000000
--- a/src/types/setting.d.ts
+++ /dev/null
@@ -1,70 +0,0 @@
-declare type DefaultSettings = {
-  /**
-   * 缃戦〉鏍囬
-   */
-  title?: string;
-
-  /**
-   * 渚ц竟鏍忎富棰� theme-dark | theme-light
-   */
-  sideTheme?: string;
-
-  /**
-   * 鏄惁鏄剧ず绯荤粺甯冨眬璁剧疆
-   */
-  showSettings?: boolean;
-
-  /**
-   * 鏄惁鏄剧ず椤堕儴瀵艰埅
-   */
-  topNav?: boolean;
-
-  /**
-   * 鏄惁鏄剧ず澶氭爣绛惧鑸�
-   */
-  tagsView?: boolean;
-  /**
-   * 鏄惁鍥哄畾澶撮儴
-   */
-  fixedHeader?: boolean;
-  /**
-   * 鏄惁鏄剧ず渚ц竟鏍廘ogo
-   */
-  sidebarLogo?: boolean;
-  /**
-   * 瀵艰埅鏍忓竷灞�
-   */
-  layout?: string;
-  /**
-   * 涓婚妯″紡
-   */
-  theme?: string;
-
-  /**
-   * 甯冨眬澶у皬
-   */
-  size?: string;
-
-  /**
-   * 璇█
-   */
-  language?: string;
-
-  /**
-   * 鏄惁鏄剧ず鍔ㄦ�佹爣棰�
-   */
-  dynamicTitle?: boolean;
-  /**
-   * 鏄惁鍚敤鍔ㄧ敾鏁堟灉
-   */
-  animationEnable?: boolean;
-  /**
-   *  鏄惁鍚敤鏆楅粦妯″紡
-   *
-   * true:鏆楅粦妯″紡
-   * false: 鏄庝寒妯″紡
-   */
-  dark?: boolean;
-
-  errorLog?: string;
-};
diff --git a/src/types/vform3-builds.d.ts b/src/types/vform3-builds.d.ts
deleted file mode 100644
index 82573de..0000000
--- a/src/types/vform3-builds.d.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-declare module 'vform3-builds' {
-  const content: any;
-  export = content;
-}
diff --git a/src/utils/index.ts b/src/utils/index.ts
index f474462..2b0aad5 100644
--- a/src/utils/index.ts
+++ b/src/utils/index.ts
@@ -89,7 +89,7 @@
  * @returns {Array}
  */
 export const cleanArray = (actual: Array<any>) => {
-  const newArray = [];
+  const newArray: any[] = [];
   for (let i = 0; i < actual.length; i++) {
     if (actual[i]) {
       newArray.push(actual[i]);
diff --git a/src/utils/propTypes.ts b/src/utils/propTypes.ts
index 1847040..24d861d 100644
--- a/src/utils/propTypes.ts
+++ b/src/utils/propTypes.ts
@@ -3,6 +3,7 @@
 
 type PropTypes = VueTypesInterface & {
   readonly style: VueTypeValidableDef<CSSProperties>;
+  readonly fieldOption: VueTypeValidableDef<Array<FieldOption>>;
 };
 
 const propTypes = createTypes({
diff --git a/src/utils/request.ts b/src/utils/request.ts
index a183ca6..f2a69f0 100644
--- a/src/utils/request.ts
+++ b/src/utils/request.ts
@@ -36,11 +36,12 @@
     // 瀵瑰簲鍥介檯鍖栬祫婧愭枃浠跺悗缂�
     config.headers['Content-Language'] = getLanguage();
 
-    const isToken = (config.headers || {}).isToken === false;
+    const isToken = config.headers?.isToken === false;
     // 鏄惁闇�瑕侀槻姝㈡暟鎹噸澶嶆彁浜�
-    const isRepeatSubmit = (config.headers || {}).repeatSubmit === false;
+    const isRepeatSubmit = config.headers?.repeatSubmit === false;
     // 鏄惁闇�瑕佸姞瀵�
-    const isEncrypt = (config.headers || {}).isEncrypt === 'true';
+    const isEncrypt = config.headers?.isEncrypt === 'true';
+
     if (getToken() && !isToken) {
       config.headers['Authorization'] = 'Bearer ' + getToken(); // 璁╂瘡涓姹傛惡甯﹁嚜瀹氫箟token 璇锋牴鎹疄闄呮儏鍐佃嚜琛屼慨鏀�
     }
@@ -75,12 +76,14 @@
         }
       }
     }
-    // 褰撳紑鍚弬鏁板姞瀵�
-    if (isEncrypt && (config.method === 'post' || config.method === 'put')) {
-      // 鐢熸垚涓�涓� AES 瀵嗛挜
-      const aesKey = generateAesKey();
-      config.headers[encryptHeader] = encrypt(encryptBase64(aesKey));
-      config.data = typeof config.data === 'object' ? encryptWithAes(JSON.stringify(config.data), aesKey) : encryptWithAes(config.data, aesKey);
+    if (import.meta.env.VITE_APP_ENCRYPT === 'true') {
+      // 褰撳紑鍚弬鏁板姞瀵�
+      if (isEncrypt && (config.method === 'post' || config.method === 'put')) {
+        // 鐢熸垚涓�涓� AES 瀵嗛挜
+        const aesKey = generateAesKey();
+        config.headers[encryptHeader] = encrypt(encryptBase64(aesKey));
+        config.data = typeof config.data === 'object' ? encryptWithAes(JSON.stringify(config.data), aesKey) : encryptWithAes(config.data, aesKey);
+      }
     }
     // FormData鏁版嵁鍘昏姹傚ごContent-Type
     if (config.data instanceof FormData) {
@@ -89,7 +92,6 @@
     return config;
   },
   (error: any) => {
-    console.log(error);
     return Promise.reject(error);
   }
 );
@@ -97,19 +99,21 @@
 // 鍝嶅簲鎷︽埅鍣�
 service.interceptors.response.use(
   (res: AxiosResponse) => {
-    // 鍔犲瘑鍚庣殑 AES 绉橀挜
-    const keyStr = res.headers[encryptHeader];
-    // 鍔犲瘑
-    if (keyStr != null && keyStr != '') {
-      const data = res.data;
-      // 璇锋眰浣� AES 瑙e瘑
-      const base64Str = decrypt(keyStr);
-      // base64 瑙g爜 寰楀埌璇锋眰澶寸殑 AES 绉橀挜
-      const aesKey = decryptBase64(base64Str.toString());
-      // aesKey 瑙g爜 data
-      const decryptData = decryptWithAes(data, aesKey);
-      // 灏嗙粨鏋� (寰楀埌鐨勬槸 JSON 瀛楃涓�) 杞负 JSON
-      res.data = JSON.parse(decryptData);
+    if (import.meta.env.VITE_APP_ENCRYPT === 'true') {
+      // 鍔犲瘑鍚庣殑 AES 绉橀挜
+      const keyStr = res.headers[encryptHeader];
+      // 鍔犲瘑
+      if (keyStr != null && keyStr != '') {
+        const data = res.data;
+        // 璇锋眰浣� AES 瑙e瘑
+        const base64Str = decrypt(keyStr);
+        // base64 瑙g爜 寰楀埌璇锋眰澶寸殑 AES 绉橀挜
+        const aesKey = decryptBase64(base64Str.toString());
+        // aesKey 瑙g爜 data
+        const decryptData = decryptWithAes(data, aesKey);
+        // 灏嗙粨鏋� (寰楀埌鐨勬槸 JSON 瀛楃涓�) 杞负 JSON
+        res.data = JSON.parse(decryptData);
+      }
     }
     // 鏈缃姸鎬佺爜鍒欓粯璁ゆ垚鍔熺姸鎬�
     const code = res.data.code || HttpStatus.SUCCESS;
@@ -138,7 +142,6 @@
       }
       return Promise.reject('鏃犳晥鐨勪細璇濓紝鎴栬�呬細璇濆凡杩囨湡锛岃閲嶆柊鐧诲綍銆�');
     } else if (code === HttpStatus.SERVER_ERROR) {
-      console.log(msg);
       ElMessage({ message: msg, type: 'error' });
       return Promise.reject(new Error(msg));
     } else if (code === HttpStatus.WARN) {
diff --git a/src/utils/ruoyi.ts b/src/utils/ruoyi.ts
index a116b52..8efd12c 100644
--- a/src/utils/ruoyi.ts
+++ b/src/utils/ruoyi.ts
@@ -68,7 +68,7 @@
   if (value === undefined) {
     return '';
   }
-  const actions = [];
+  const actions: Array<string | number> = [];
   Object.keys(datas).some((key) => {
     if (datas[key].value == '' + value) {
       actions.push(datas[key].label);
@@ -245,3 +245,7 @@
 export const blobValidate = (data: any) => {
   return data.type !== 'application/json';
 };
+
+export default {
+  handleTree
+};
diff --git a/src/utils/websocket.ts b/src/utils/websocket.ts
index a4529f9..d4dd8a8 100644
--- a/src/utils/websocket.ts
+++ b/src/utils/websocket.ts
@@ -19,10 +19,8 @@
  */
 
 import { getToken } from '@/utils/auth';
+import { ElNotification } from 'element-plus';
 import useNoticeStore from '@/store/modules/notice';
-import { ElNotification } from "element-plus";
-
-const { addNotice } = useNoticeStore();
 
 let socketUrl: any = ''; // socket鍦板潃
 let websocket: any = null; // websocket 瀹炰緥
@@ -125,7 +123,7 @@
     if (e.data.indexOf('ping') > 0) {
       return;
     }
-    addNotice({
+    useNoticeStore().addNotice({
       message: e.data,
       read: false,
       time: new Date().toLocaleString()
@@ -135,7 +133,7 @@
       message: e.data,
       type: 'success',
       duration: 3000
-    })
+    });
     return e.data;
   };
 };
diff --git a/src/views/demo/demo/index.vue b/src/views/demo/demo/index.vue
index a7f3bd7..523deff 100644
--- a/src/views/demo/demo/index.vue
+++ b/src/views/demo/demo/index.vue
@@ -1,23 +1,23 @@
 <template>
   <div class="p-2">
     <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
-      <div class="mb-[10px]" v-show="showSearch">
+      <div v-show="showSearch" class="mb-[10px]">
         <el-card shadow="hover">
-          <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
+          <el-form ref="queryFormRef" :model="queryParams" :inline="true">
             <el-form-item label="閮ㄩ棬id" prop="deptId">
-              <el-input v-model="queryParams.deptId" placeholder="璇疯緭鍏ラ儴闂╥d" clearable style="width: 240px" @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.deptId" placeholder="璇疯緭鍏ラ儴闂╥d" clearable @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item label="鐢ㄦ埛id" prop="userId">
-              <el-input v-model="queryParams.userId" placeholder="璇疯緭鍏ョ敤鎴穒d" clearable style="width: 240px" @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.userId" placeholder="璇疯緭鍏ョ敤鎴穒d" clearable @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item label="鎺掑簭鍙�" prop="orderNum">
-              <el-input v-model="queryParams.orderNum" placeholder="璇疯緭鍏ユ帓搴忓彿" clearable style="width: 240px" @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.orderNum" placeholder="璇疯緭鍏ユ帓搴忓彿" clearable @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item label="key閿�" prop="testKey">
-              <el-input v-model="queryParams.testKey" placeholder="璇疯緭鍏ey閿�" clearable style="width: 240px" @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.testKey" placeholder="璇疯緭鍏ey閿�" clearable @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item label="鍊�" prop="value">
-              <el-input v-model="queryParams.value" placeholder="璇疯緭鍏ュ��" clearable style="width: 240px" @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.value" placeholder="璇疯緭鍏ュ��" clearable @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item>
               <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
@@ -32,26 +32,26 @@
       <template #header>
         <el-row :gutter="10" class="mb8">
           <el-col :span="1.5">
-            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['demo:demo:add']">鏂板</el-button>
+            <el-button v-hasPermi="['demo:demo:add']" type="primary" plain icon="Plus" @click="handleAdd">鏂板</el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['demo:demo:edit']">淇敼</el-button>
+            <el-button v-hasPermi="['demo:demo:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()">淇敼</el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['demo:demo:remove']"
+            <el-button v-hasPermi="['demo:demo:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()"
               >鍒犻櫎</el-button
             >
           </el-col>
           <el-col :span="1.5">
-            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['demo:demo:export']">瀵煎嚭</el-button>
+            <el-button v-hasPermi="['demo:demo:export']" type="warning" plain icon="Download" @click="handleExport">瀵煎嚭</el-button>
           </el-col>
-          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+          <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
         </el-row>
       </template>
 
       <el-table v-loading="loading" :data="demoList" @selection-change="handleSelectionChange">
         <el-table-column type="selection" width="55" align="center" />
-        <el-table-column label="涓婚敭" align="center" prop="id" v-if="true" />
+        <el-table-column v-if="true" label="涓婚敭" align="center" prop="id" />
         <el-table-column label="閮ㄩ棬id" align="center" prop="deptId" />
         <el-table-column label="鐢ㄦ埛id" align="center" prop="userId" />
         <el-table-column label="鎺掑簭鍙�" align="center" prop="orderNum" />
@@ -60,19 +60,19 @@
         <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="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['demo:demo:edit']"></el-button>
+              <el-button v-hasPermi="['demo:demo:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
             </el-tooltip>
             <el-tooltip content="鍒犻櫎" placement="top">
-              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['demo:demo:remove']"></el-button>
+              <el-button v-hasPermi="['demo:demo:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></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" />
+      <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
     </el-card>
     <!-- 娣诲姞鎴栦慨鏀规祴璇曞崟瀵硅瘽妗� -->
-    <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+    <el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
       <el-form ref="demoFormRef" :model="form" :rules="rules" label-width="80px">
         <el-form-item label="閮ㄩ棬id" prop="deptId">
           <el-input v-model="form.deptId" placeholder="璇疯緭鍏ラ儴闂╥d" />
@@ -129,8 +129,8 @@
   userId: undefined,
   orderNum: undefined,
   testKey: undefined,
-  value: undefined,
-}
+  value: undefined
+};
 const data = reactive<PageData<DemoForm, DemoQuery>>({
   form: { ...initFormData },
   queryParams: {
@@ -140,27 +140,15 @@
     userId: undefined,
     orderNum: undefined,
     testKey: undefined,
-    value: undefined,
+    value: undefined
   },
   rules: {
-    id: [
-      { required: true, message: "涓婚敭涓嶈兘涓虹┖", trigger: "blur" }
-    ],
-    deptId: [
-      { required: true, message: "閮ㄩ棬id涓嶈兘涓虹┖", trigger: "blur" }
-    ],
-    userId: [
-      { required: true, message: "鐢ㄦ埛id涓嶈兘涓虹┖", trigger: "blur" }
-    ],
-    orderNum: [
-      { required: true, message: "鎺掑簭鍙蜂笉鑳戒负绌�", trigger: "blur" }
-    ],
-    testKey: [
-      { required: true, message: "key閿笉鑳戒负绌�", trigger: "blur" }
-    ],
-    value: [
-      { required: true, message: "鍊间笉鑳戒负绌�", trigger: "blur" }
-    ],
+    id: [{ required: true, message: '涓婚敭涓嶈兘涓虹┖', trigger: 'blur' }],
+    deptId: [{ required: true, message: '閮ㄩ棬id涓嶈兘涓虹┖', trigger: 'blur' }],
+    userId: [{ required: true, message: '鐢ㄦ埛id涓嶈兘涓虹┖', trigger: 'blur' }],
+    orderNum: [{ required: true, message: '鎺掑簭鍙蜂笉鑳戒负绌�', trigger: 'blur' }],
+    testKey: [{ required: true, message: 'key閿笉鑳戒负绌�', trigger: 'blur' }],
+    value: [{ required: true, message: '鍊间笉鑳戒负绌�', trigger: 'blur' }]
   }
 });
 
@@ -173,55 +161,55 @@
   demoList.value = res.rows;
   total.value = res.total;
   loading.value = false;
-}
+};
 
 /** 鍙栨秷鎸夐挳 */
 const cancel = () => {
   reset();
   dialog.visible = false;
-}
+};
 
 /** 琛ㄥ崟閲嶇疆 */
 const reset = () => {
   form.value = { ...initFormData };
   demoFormRef.value?.resetFields();
-}
+};
 
 /** 鎼滅储鎸夐挳鎿嶄綔 */
 const handleQuery = () => {
   queryParams.value.pageNum = 1;
   getList();
-}
+};
 
 /** 閲嶇疆鎸夐挳鎿嶄綔 */
 const resetQuery = () => {
   queryFormRef.value?.resetFields();
   handleQuery();
-}
+};
 
 /** 澶氶�夋閫変腑鏁版嵁 */
 const handleSelectionChange = (selection: DemoVO[]) => {
-  ids.value = selection.map(item => item.id);
+  ids.value = selection.map((item) => item.id);
   single.value = selection.length != 1;
   multiple.value = !selection.length;
-}
+};
 
 /** 鏂板鎸夐挳鎿嶄綔 */
 const handleAdd = () => {
   reset();
   dialog.visible = true;
-  dialog.title = "娣诲姞娴嬭瘯鍗�";
-}
+  dialog.title = '娣诲姞娴嬭瘯鍗�';
+};
 
 /** 淇敼鎸夐挳鎿嶄綔 */
 const handleUpdate = async (row?: DemoVO) => {
   reset();
-  const _id = row?.id || ids.value[0]
+  const _id = row?.id || ids.value[0];
   const res = await getDemo(_id);
   Object.assign(form.value, res.data);
   dialog.visible = true;
-  dialog.title = "淇敼娴嬭瘯鍗�";
-}
+  dialog.title = '淇敼娴嬭瘯鍗�';
+};
 
 /** 鎻愪氦鎸夐挳 */
 const submitForm = () => {
@@ -229,32 +217,36 @@
     if (valid) {
       buttonLoading.value = true;
       if (form.value.id) {
-        await updateDemo(form.value).finally(() => buttonLoading.value = false);
+        await updateDemo(form.value).finally(() => (buttonLoading.value = false));
       } else {
-        await addDemo(form.value).finally(() => buttonLoading.value = false);
+        await addDemo(form.value).finally(() => (buttonLoading.value = false));
       }
-      proxy?.$modal.msgSuccess("淇敼鎴愬姛");
+      proxy?.$modal.msgSuccess('淇敼鎴愬姛');
       dialog.visible = false;
       await getList();
     }
   });
-}
+};
 
 /** 鍒犻櫎鎸夐挳鎿嶄綔 */
 const handleDelete = async (row?: DemoVO) => {
   const _ids = row?.id || ids.value;
-  await proxy?.$modal.confirm('鏄惁纭鍒犻櫎娴嬭瘯鍗曠紪鍙蜂负"' + _ids + '"鐨勬暟鎹」锛�').finally(() => loading.value = false);
+  await proxy?.$modal.confirm('鏄惁纭鍒犻櫎娴嬭瘯鍗曠紪鍙蜂负"' + _ids + '"鐨勬暟鎹」锛�').finally(() => (loading.value = false));
   await delDemo(_ids);
-  proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+  proxy?.$modal.msgSuccess('鍒犻櫎鎴愬姛');
   await getList();
-}
+};
 
 /** 瀵煎嚭鎸夐挳鎿嶄綔 */
 const handleExport = () => {
-  proxy?.download('demo/demo/export', {
-    ...queryParams.value
-  }, `demo_${new Date().getTime()}.xlsx`)
-}
+  proxy?.download(
+    'demo/demo/export',
+    {
+      ...queryParams.value
+    },
+    `demo_${new Date().getTime()}.xlsx`
+  );
+};
 
 onMounted(() => {
   getList();
diff --git a/src/views/demo/tree/index.vue b/src/views/demo/tree/index.vue
index d79f817..921ddd9 100644
--- a/src/views/demo/tree/index.vue
+++ b/src/views/demo/tree/index.vue
@@ -1,11 +1,11 @@
 <template>
   <div class="p-2">
     <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
-      <div class="mb-[10px]" v-show="showSearch">
+      <div v-show="showSearch" class="mb-[10px]">
         <el-card shadow="hover">
-          <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
+          <el-form ref="queryFormRef" :model="queryParams" :inline="true">
             <el-form-item label="鏍戣妭鐐瑰悕" prop="treeName">
-              <el-input v-model="queryParams.treeName" placeholder="璇疯緭鍏ユ爲鑺傜偣鍚�" clearable style="width: 240px" @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.treeName" placeholder="璇疯緭鍏ユ爲鑺傜偣鍚�" clearable @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item>
               <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
@@ -20,21 +20,21 @@
       <template #header>
         <el-row :gutter="10" class="mb8">
           <el-col :span="1.5">
-            <el-button type="primary" plain icon="Plus" @click="handleAdd()" v-hasPermi="['demo:tree:add']">鏂板</el-button>
+            <el-button v-hasPermi="['demo:tree:add']" type="primary" plain icon="Plus" @click="handleAdd()">鏂板</el-button>
           </el-col>
           <el-col :span="1.5">
             <el-button type="info" plain icon="Sort" @click="handleToggleExpandAll">灞曞紑/鎶樺彔</el-button>
           </el-col>
-          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+          <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
         </el-row>
       </template>
       <el-table
+        ref="treeTableRef"
         v-loading="loading"
         :data="treeList"
         row-key="id"
         :default-expand-all="isExpandAll"
-        :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
-        ref="treeTableRef"
+        :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
       >
         <el-table-column label="鐖秈d" align="center" prop="parentId" />
         <el-table-column label="閮ㄩ棬id" align="center" prop="deptId" />
@@ -43,20 +43,20 @@
         <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="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['demo:tree:edit']" />
+              <el-button v-hasPermi="['demo:tree:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)" />
             </el-tooltip>
             <el-tooltip content="鏂板" placement="top">
-              <el-button link type="primary" icon="Plus" @click="handleAdd(scope.row)" v-hasPermi="['demo:tree:add']" />
+              <el-button v-hasPermi="['demo:tree:add']" link type="primary" icon="Plus" @click="handleAdd(scope.row)" />
             </el-tooltip>
             <el-tooltip content="鍒犻櫎" placement="top">
-              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['demo:tree:remove']" />
+              <el-button v-hasPermi="['demo:tree:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)" />
             </el-tooltip>
           </template>
         </el-table-column>
       </el-table>
     </el-card>
     <!-- 娣诲姞鎴栦慨鏀规祴璇曟爲瀵硅瘽妗� -->
-    <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+    <el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
       <el-form ref="treeFormRef" :model="form" :rules="rules" label-width="80px">
         <el-form-item label="鐖秈d" prop="parentId">
           <el-tree-select
@@ -89,18 +89,16 @@
 </template>
 
 <script setup name="Tree" lang="ts">
-import { listTree, getTree, delTree, addTree, updateTree } from "@/api/demo/tree";
+import { listTree, getTree, delTree, addTree, updateTree } from '@/api/demo/tree';
 import { TreeVO, TreeQuery, TreeForm } from '@/api/demo/tree/types';
-
 
 type TreeOption = {
   id: number;
   treeName: string;
   children?: TreeOption[];
-}
+};
 
-const { proxy } = getCurrentInstance() as ComponentInternalInstance;;
-
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
 const treeList = ref<TreeVO[]>([]);
 const treeOptions = ref<TreeOption[]>([]);
@@ -111,46 +109,35 @@
 
 const queryFormRef = ref<ElFormInstance>();
 const treeFormRef = ref<ElFormInstance>();
-const treeTableRef = ref<ElTableInstance>()
+const treeTableRef = ref<ElTableInstance>();
 
 const dialog = reactive<DialogOption>({
-    visible: false,
-    title: ''
+  visible: false,
+  title: ''
 });
 
-
 const initFormData: TreeForm = {
-    id: undefined,
-    parentId: undefined,
-    deptId: undefined,
-    userId: undefined,
-    treeName: undefined,
-}
+  id: undefined,
+  parentId: undefined,
+  deptId: undefined,
+  userId: undefined,
+  treeName: undefined
+};
 
 const data = reactive<PageData<TreeForm, TreeQuery>>({
-  form: {...initFormData},
+  form: { ...initFormData },
   queryParams: {
     parentId: undefined,
     deptId: undefined,
     userId: undefined,
-    treeName: undefined,
+    treeName: undefined
   },
   rules: {
-    id: [
-      { required: true, message: "涓婚敭涓嶈兘涓虹┖", trigger: "blur" }
-    ],
-    parentId: [
-      { required: true, message: "鐖秈d涓嶈兘涓虹┖", trigger: "blur" }
-    ],
-    deptId: [
-      { required: true, message: "閮ㄩ棬id涓嶈兘涓虹┖", trigger: "blur" }
-    ],
-    userId: [
-      { required: true, message: "鐢ㄦ埛id涓嶈兘涓虹┖", trigger: "blur" }
-    ],
-    treeName: [
-      { required: true, message: "鍊间笉鑳戒负绌�", trigger: "blur" }
-    ],
+    id: [{ required: true, message: '涓婚敭涓嶈兘涓虹┖', trigger: 'blur' }],
+    parentId: [{ required: true, message: '鐖秈d涓嶈兘涓虹┖', trigger: 'blur' }],
+    deptId: [{ required: true, message: '閮ㄩ棬id涓嶈兘涓虹┖', trigger: 'blur' }],
+    userId: [{ required: true, message: '鐢ㄦ埛id涓嶈兘涓虹┖', trigger: 'blur' }],
+    treeName: [{ required: true, message: '鍊间笉鑳戒负绌�', trigger: 'blur' }]
   }
 });
 
@@ -160,44 +147,44 @@
 const getList = async () => {
   loading.value = true;
   const res = await listTree(queryParams.value);
-  const data = proxy?.handleTree<TreeVO>(res.data, "id", "parentId");
+  const data = proxy?.handleTree<TreeVO>(res.data, 'id', 'parentId');
   if (data) {
     treeList.value = data;
     loading.value = false;
   }
-}
+};
 
 /** 鏌ヨ娴嬭瘯鏍戜笅鎷夋爲缁撴瀯 */
 const getTreeselect = async () => {
   const res = await listTree();
   treeOptions.value = [];
   const data: TreeOption = { id: 0, treeName: '椤剁骇鑺傜偣', children: [] };
-  data.children = proxy?.handleTree<TreeOption>(res.data, "id", "parentId");
+  data.children = proxy?.handleTree<TreeOption>(res.data, 'id', 'parentId');
   treeOptions.value.push(data);
-}
+};
 
 // 鍙栨秷鎸夐挳
 const cancel = () => {
   reset();
   dialog.visible = false;
-}
+};
 
 // 琛ㄥ崟閲嶇疆
 const reset = () => {
-  form.value = {...initFormData}
+  form.value = { ...initFormData };
   treeFormRef.value?.resetFields();
-}
+};
 
 /** 鎼滅储鎸夐挳鎿嶄綔 */
 const handleQuery = () => {
   getList();
-}
+};
 
 /** 閲嶇疆鎸夐挳鎿嶄綔 */
 const resetQuery = () => {
   queryFormRef.value?.resetFields();
   handleQuery();
-}
+};
 
 /** 鏂板鎸夐挳鎿嶄綔 */
 const handleAdd = (row?: TreeVO) => {
@@ -209,22 +196,22 @@
     form.value.parentId = 0;
   }
   dialog.visible = true;
-  dialog.title = "娣诲姞娴嬭瘯鏍�";
-}
+  dialog.title = '娣诲姞娴嬭瘯鏍�';
+};
 
 /** 灞曞紑/鎶樺彔鎿嶄綔 */
 const handleToggleExpandAll = () => {
   isExpandAll.value = !isExpandAll.value;
-  toggleExpandAll(treeList.value, isExpandAll.value)
-}
+  toggleExpandAll(treeList.value, isExpandAll.value);
+};
 
 /** 灞曞紑/鎶樺彔鎿嶄綔 */
 const toggleExpandAll = (data: TreeVO[], status: boolean) => {
   data.forEach((item) => {
-    treeTableRef.value?.toggleRowExpansion(item, status)
-    if (item.children && item.children.length > 0) toggleExpandAll(item.children, status)
-  })
-}
+    treeTableRef.value?.toggleRowExpansion(item, status);
+    if (item.children && item.children.length > 0) toggleExpandAll(item.children, status);
+  });
+};
 
 /** 淇敼鎸夐挳鎿嶄綔 */
 const handleUpdate = async (row: TreeVO) => {
@@ -236,8 +223,8 @@
   const res = await getTree(row.id);
   Object.assign(form.value, res.data);
   dialog.visible = true;
-  dialog.title = "淇敼娴嬭瘯鏍�";
-}
+  dialog.title = '淇敼娴嬭瘯鏍�';
+};
 
 /** 鎻愪氦鎸夐挳 */
 const submitForm = () => {
@@ -245,25 +232,25 @@
     if (valid) {
       buttonLoading.value = true;
       if (form.value.id) {
-        await updateTree(form.value).finally(() => buttonLoading.value = false);
+        await updateTree(form.value).finally(() => (buttonLoading.value = false));
       } else {
-        await addTree(form.value).finally(() => buttonLoading.value = false);
+        await addTree(form.value).finally(() => (buttonLoading.value = false));
       }
-      proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+      proxy?.$modal.msgSuccess('鎿嶄綔鎴愬姛');
       dialog.visible = false;
       await getList();
     }
   });
-}
+};
 
 /** 鍒犻櫎鎸夐挳鎿嶄綔 */
 const handleDelete = async (row: TreeVO) => {
   await proxy?.$modal.confirm('鏄惁纭鍒犻櫎娴嬭瘯鏍戠紪鍙蜂负"' + row.id + '"鐨勬暟鎹」锛�');
   loading.value = true;
-  await delTree(row.id).finally(() => loading.value = false);
+  await delTree(row.id).finally(() => (loading.value = false));
   await getList();
-  proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-}
+  proxy?.$modal.msgSuccess('鍒犻櫎鎴愬姛');
+};
 
 onMounted(() => {
   getList();
diff --git a/src/views/error/401.vue b/src/views/error/401.vue
index 1d9c76e..968550c 100644
--- a/src/views/error/401.vue
+++ b/src/views/error/401.vue
@@ -24,11 +24,11 @@
 
 let { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
-const errGif = ref(errImage + "?" + +new Date());
+const errGif = ref(errImage + '?' + +new Date());
 
 function back() {
   if (proxy?.$route.query.noGoBack) {
-    proxy.$router.push({ path: "/" });
+    proxy.$router.push({ path: '/' });
   } else {
     proxy?.$router.go(-1);
   }
diff --git a/src/views/error/404.vue b/src/views/error/404.vue
index 94ed936..b9a9ea3 100644
--- a/src/views/error/404.vue
+++ b/src/views/error/404.vue
@@ -23,13 +23,13 @@
 
 <script setup lang="ts">
 let message = computed(() => {
-  return '鎵句笉鍒扮綉椤碉紒'
-})
+  return '鎵句笉鍒扮綉椤碉紒';
+});
 </script>
 
 <style lang="scss" scoped>
-.wscn-http404-container{
-  transform: translate(-50%,-50%);
+.wscn-http404-container {
+  transform: translate(-50%, -50%);
   position: absolute;
   top: 40%;
   left: 50%;
diff --git a/src/views/index.vue b/src/views/index.vue
index 12d764b..249a41b 100644
--- a/src/views/index.vue
+++ b/src/views/index.vue
@@ -21,7 +21,7 @@
           * 鍒嗗竷寮忛攣 Lock4j 娉ㄨВ閿併�佸伐鍏烽攣 澶氱澶氭牱<br />
           * 鍒嗗竷寮忓箓绛� Lock4j 鍩轰簬鍒嗗竷寮忛攣瀹炵幇<br />
           * 鍒嗗竷寮忛摼璺拷韪� SkyWalking 鏀寔閾捐矾杩借釜銆佺綉鏍煎垎鏋愩�佸害閲忚仛鍚堛�佸彲瑙嗗寲<br />
-          * 鍒嗗竷寮忎换鍔¤皟搴� PowerJob 楂樻�ц兘 楂樺彲闈� 鏄撴墿灞�<br />
+          * 鍒嗗竷寮忎换鍔¤皟搴� SnailJob 楂樻�ц兘 楂樺彲闈� 鏄撴墿灞�<br />
           * 鏂囦欢瀛樺偍 Minio 鏈湴瀛樺偍<br />
           * 鏂囦欢瀛樺偍 涓冪墰銆侀樋閲屻�佽吘璁� 浜戝瓨鍌�<br />
           * 鐩戞帶妗嗘灦 SpringBoot-Admin 鍏ㄦ柟浣嶆湇鍔$洃鎺�<br />
@@ -33,7 +33,7 @@
           * 閮ㄧ讲鏂瑰紡 Docker 瀹瑰櫒缂栨帓 涓�閿儴缃蹭笟鍔¢泦缇�<br />
           * 鍥介檯鍖� SpringMessage Spring鏍囧噯鍥介檯鍖栨柟妗�<br />
         </p>
-        <p><b>褰撳墠鐗堟湰:</b> <span>v5.1.2</span></p>
+        <p><b>褰撳墠鐗堟湰:</b> <span>v5.2.0-BETA</span></p>
         <p>
           <el-tag type="danger">&yen;鍏嶈垂寮�婧�</el-tag>
         </p>
@@ -78,7 +78,7 @@
           * 鍒嗗竷寮忕洃鎺� Prometheus銆丟rafana 鍏ㄦ柟浣嶆�ц兘鐩戞帶<br />
           * 鍏朵綑涓� Vue 鐗堟湰涓�鑷�<br />
         </p>
-        <p><b>褰撳墠鐗堟湰:</b> <span>v2.1.2</span></p>
+        <p><b>褰撳墠鐗堟湰:</b> <span>v2.2.0-BETA</span></p>
         <p>
           <el-tag type="danger">&yen;鍏嶈垂寮�婧�</el-tag>
         </p>
@@ -96,16 +96,9 @@
 </template>
 
 <script setup name="Index" lang="ts">
-import { initWebSocket } from '@/utils/websocket';
-
-onMounted(() => {
-  let protocol = window.location.protocol === 'https:' ? 'wss://' : 'ws://'
-  initWebSocket(protocol + window.location.host + import.meta.env.VITE_APP_BASE_API + "/resource/websocket");
-});
-
-const goTarget = (url:string) => {
-  window.open(url, '__blank')
-}
+const goTarget = (url: string) => {
+  window.open(url, '__blank');
+};
 </script>
 
 <style scoped lang="scss">
@@ -131,7 +124,7 @@
     margin: 0;
   }
 
-  font-family: "open sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
+  font-family: 'open sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
   font-size: 13px;
   color: #676a6c;
   overflow-x: hidden;
diff --git a/src/views/login.vue b/src/views/login.vue
index e5c73fa..7e1394e 100644
--- a/src/views/login.vue
+++ b/src/views/login.vue
@@ -2,7 +2,7 @@
   <div class="login">
     <el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form">
       <h3 class="title">RuoYi-Vue-Plus澶氱鎴风鐞嗙郴缁�</h3>
-      <el-form-item prop="tenantId" v-if="tenantEnabled">
+      <el-form-item v-if="tenantEnabled" prop="tenantId">
         <el-select v-model="loginForm.tenantId" filterable placeholder="璇烽�夋嫨/杈撳叆鍏徃鍚嶇О" style="width: 100%">
           <el-option v-for="item in tenantList" :key="item.tenantId" :label="item.companyName" :value="item.tenantId"></el-option>
           <template #prefix><svg-icon icon-class="company" class="el-input__icon input-icon" /></template>
@@ -18,21 +18,24 @@
           <template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template>
         </el-input>
       </el-form-item>
-      <el-form-item prop="code" v-if="captchaEnabled">
+      <el-form-item v-if="captchaEnabled" prop="code">
         <el-input v-model="loginForm.code" size="large" auto-complete="off" placeholder="楠岃瘉鐮�" style="width: 63%" @keyup.enter="handleLogin">
           <template #prefix><svg-icon icon-class="validCode" class="el-input__icon input-icon" /></template>
         </el-input>
         <div class="login-code">
-          <img :src="codeUrl" @click="getCode" class="login-code-img" />
+          <img :src="codeUrl" class="login-code-img" @click="getCode" />
         </div>
       </el-form-item>
-      <el-checkbox v-model="loginForm.rememberMe" style="margin:0px 0px 25px 0px;">璁颁綇瀵嗙爜</el-checkbox>
-      <el-form-item style="float: right;">
+      <el-checkbox v-model="loginForm.rememberMe" style="margin: 0 0 25px 0">璁颁綇瀵嗙爜</el-checkbox>
+      <el-form-item style="float: right">
         <el-button circle title="寰俊鐧诲綍" @click="doSocialLogin('wechat')">
           <svg-icon icon-class="wechat" />
         </el-button>
         <el-button circle title="MaxKey鐧诲綍" @click="doSocialLogin('maxkey')">
           <svg-icon icon-class="maxkey" />
+        </el-button>
+        <el-button circle title="TopIam鐧诲綍" @click="doSocialLogin('topiam')">
+          <svg-icon icon-class="topiam" />
         </el-button>
         <el-button circle title="Gitee鐧诲綍" @click="doSocialLogin('gitee')">
           <svg-icon icon-class="gitee" />
@@ -41,19 +44,19 @@
           <svg-icon icon-class="github" />
         </el-button>
       </el-form-item>
-      <el-form-item style="width:100%;">
-        <el-button :loading="loading" size="large" type="primary" style="width:100%;" @click.prevent="handleLogin">
+      <el-form-item style="width: 100%">
+        <el-button :loading="loading" size="large" type="primary" style="width: 100%" @click.prevent="handleLogin">
           <span v-if="!loading">鐧� 褰�</span>
           <span v-else>鐧� 褰� 涓�...</span>
         </el-button>
-        <div style="float: right;" v-if="register">
+        <div v-if="register" style="float: right">
           <router-link class="link-type" :to="'/register'">绔嬪嵆娉ㄥ唽</router-link>
         </div>
       </el-form-item>
     </el-form>
     <!--  搴曢儴  -->
     <div class="el-login-footer">
-      <span>Copyright 漏 2018-2023 鐤媯鐨勭嫯瀛怢i All Rights Reserved.</span>
+      <span>Copyright 漏 2018-2024 鐤媯鐨勭嫯瀛怢i All Rights Reserved.</span>
     </div>
   </div>
 </template>
@@ -64,7 +67,7 @@
 import { useUserStore } from '@/store/modules/user';
 import { LoginData, TenantVO } from '@/api/types';
 import { to } from 'await-to-js';
-import { HttpStatus } from "@/enums/RespEnum";
+import { HttpStatus } from '@/enums/RespEnum';
 
 const userStore = useUserStore();
 const router = useRouter();
@@ -79,7 +82,7 @@
 } as LoginData);
 
 const loginRules: ElFormRules = {
-  tenantId: [{ required: true, trigger: "blur", message: "璇疯緭鍏ユ偍鐨勭鎴风紪鍙�" }],
+  tenantId: [{ required: true, trigger: 'blur', message: '璇疯緭鍏ユ偍鐨勭鎴风紪鍙�' }],
   username: [{ required: true, trigger: 'blur', message: '璇疯緭鍏ユ偍鐨勮处鍙�' }],
   password: [{ required: true, trigger: 'blur', message: '璇疯緭鍏ユ偍鐨勫瘑鐮�' }],
   code: [{ required: true, trigger: 'change', message: '璇疯緭鍏ラ獙璇佺爜' }]
@@ -92,7 +95,6 @@
 // 绉熸埛寮�鍏�
 const tenantEnabled = ref(true);
 
-
 // 娉ㄥ唽寮�鍏�
 const register = ref(false);
 const redirect = ref(undefined);
@@ -100,9 +102,13 @@
 // 绉熸埛鍒楄〃
 const tenantList = ref<TenantVO[]>([]);
 
-watch(() => router.currentRoute.value, (newRoute: any) => {
-  redirect.value = newRoute.query && newRoute.query.redirect;
-}, { immediate: true });
+watch(
+  () => router.currentRoute.value,
+  (newRoute: any) => {
+    redirect.value = newRoute.query && newRoute.query.redirect;
+  },
+  { immediate: true }
+);
 
 const handleLogin = () => {
   loginRef.value?.validate(async (valid: boolean, fields: any) => {
@@ -110,13 +116,13 @@
       loading.value = true;
       // 鍕鹃�変簡闇�瑕佽浣忓瘑鐮佽缃湪 localStorage 涓缃浣忕敤鎴峰悕鍜屽瘑鐮�
       if (loginForm.value.rememberMe) {
-        localStorage.setItem("tenantId", String(loginForm.value.tenantId));
+        localStorage.setItem('tenantId', String(loginForm.value.tenantId));
         localStorage.setItem('username', String(loginForm.value.username));
         localStorage.setItem('password', String(loginForm.value.password));
         localStorage.setItem('rememberMe', String(loginForm.value.rememberMe));
       } else {
         // 鍚﹀垯绉婚櫎
-        localStorage.removeItem("tenantId");
+        localStorage.removeItem('tenantId');
         localStorage.removeItem('username');
         localStorage.removeItem('password');
         localStorage.removeItem('rememberMe');
@@ -124,7 +130,8 @@
       // 璋冪敤action鐨勭櫥褰曟柟娉�
       const [err] = await to(userStore.login(loginForm.value));
       if (!err) {
-        await router.push({ path: redirect.value || '/' });
+        const redirectUrl = redirect.value || '/';
+        await router.push(redirectUrl);
         loading.value = false;
       } else {
         loading.value = false;
@@ -153,7 +160,7 @@
 };
 
 const getLoginData = () => {
-  const tenantId = localStorage.getItem("tenantId");
+  const tenantId = localStorage.getItem('tenantId');
   const username = localStorage.getItem('username');
   const password = localStorage.getItem('password');
   const rememberMe = localStorage.getItem('rememberMe');
@@ -163,8 +170,7 @@
     password: password === null ? String(loginForm.value.password) : String(password),
     rememberMe: rememberMe === null ? false : Boolean(rememberMe)
   } as LoginData;
-}
-
+};
 
 /**
  * 鑾峰彇绉熸埛鍒楄〃
@@ -178,12 +184,15 @@
       loginForm.value.tenantId = tenantList.value[0].tenantId;
     }
   }
-}
+};
 
 //妫�娴嬬鎴烽�夋嫨妗嗙殑鍙樺寲
-watch(() => loginForm.value.tenantId, () => {
-  localStorage.setItem("tenantId", String(loginForm.value.tenantId))
-});
+watch(
+  () => loginForm.value.tenantId,
+  () => {
+    localStorage.setItem('tenantId', String(loginForm.value.tenantId));
+  }
+);
 
 /**
  * 绗笁鏂圭櫥褰�
@@ -200,8 +209,6 @@
   });
 };
 
-
-
 onMounted(() => {
   getCode();
   initTenantList();
@@ -215,7 +222,7 @@
   justify-content: center;
   align-items: center;
   height: 100%;
-  background-image: url("../assets/images/login-background.jpg");
+  background-image: url('../assets/images/login-background.jpg');
   background-size: cover;
 }
 
diff --git a/src/views/monitor/cache/index.vue b/src/views/monitor/cache/index.vue
index 8719c94..81bbf2d 100644
--- a/src/views/monitor/cache/index.vue
+++ b/src/views/monitor/cache/index.vue
@@ -4,8 +4,8 @@
       <el-col :span="24" class="card-box">
         <el-card shadow="hover">
           <template #header>
-            <Monitor style="width: 1em; height: 1em; vertical-align: middle;" />
-            <span style="vertical-align: middle;">鍩烘湰淇℃伅</span>
+            <Monitor style="width: 1em; height: 1em; vertical-align: middle" />
+            <span style="vertical-align: middle">鍩烘湰淇℃伅</span>
           </template>
 
           <div class="el-table el-table--enable-row-hover el-table--medium">
@@ -16,25 +16,25 @@
                     <div class="cell">Redis鐗堟湰</div>
                   </td>
                   <td class="el-table__cell is-leaf">
-                    <div class="cell" v-if="cache.info">{{ cache.info.redis_version }}</div>
+                    <div v-if="cache.info" class="cell">{{ cache.info.redis_version }}</div>
                   </td>
                   <td class="el-table__cell is-leaf">
                     <div class="cell">杩愯妯″紡</div>
                   </td>
                   <td class="el-table__cell is-leaf">
-                    <div class="cell" v-if="cache.info">{{ cache.info.redis_mode === "standalone" ? "鍗曟満" : "闆嗙兢" }}</div>
+                    <div v-if="cache.info" class="cell">{{ cache.info.redis_mode === 'standalone' ? '鍗曟満' : '闆嗙兢' }}</div>
                   </td>
                   <td class="el-table__cell is-leaf">
                     <div class="cell">绔彛</div>
                   </td>
                   <td class="el-table__cell is-leaf">
-                    <div class="cell" v-if="cache.info">{{ cache.info.tcp_port }}</div>
+                    <div v-if="cache.info" class="cell">{{ cache.info.tcp_port }}</div>
                   </td>
                   <td class="el-table__cell is-leaf">
                     <div class="cell">瀹㈡埛绔暟</div>
                   </td>
                   <td class="el-table__cell is-leaf">
-                    <div class="cell" v-if="cache.info">{{ cache.info.connected_clients }}</div>
+                    <div v-if="cache.info" class="cell">{{ cache.info.connected_clients }}</div>
                   </td>
                 </tr>
                 <tr>
@@ -42,25 +42,25 @@
                     <div class="cell">杩愯鏃堕棿(澶�)</div>
                   </td>
                   <td class="el-table__cell is-leaf">
-                    <div class="cell" v-if="cache.info">{{ cache.info.uptime_in_days }}</div>
+                    <div v-if="cache.info" class="cell">{{ cache.info.uptime_in_days }}</div>
                   </td>
                   <td class="el-table__cell is-leaf">
                     <div class="cell">浣跨敤鍐呭瓨</div>
                   </td>
                   <td class="el-table__cell is-leaf">
-                    <div class="cell" v-if="cache.info">{{ cache.info.used_memory_human }}</div>
+                    <div v-if="cache.info" class="cell">{{ cache.info.used_memory_human }}</div>
                   </td>
                   <td class="el-table__cell is-leaf">
                     <div class="cell">浣跨敤CPU</div>
                   </td>
                   <td class="el-table__cell is-leaf">
-                    <div class="cell" v-if="cache.info">{{ parseFloat(cache.info.used_cpu_user_children).toFixed(2) }}</div>
+                    <div v-if="cache.info" class="cell">{{ parseFloat(cache.info.used_cpu_user_children).toFixed(2) }}</div>
                   </td>
                   <td class="el-table__cell is-leaf">
                     <div class="cell">鍐呭瓨閰嶇疆</div>
                   </td>
                   <td class="el-table__cell is-leaf">
-                    <div class="cell" v-if="cache.info">{{ cache.info.maxmemory_human }}</div>
+                    <div v-if="cache.info" class="cell">{{ cache.info.maxmemory_human }}</div>
                   </td>
                 </tr>
                 <tr>
@@ -68,25 +68,25 @@
                     <div class="cell">AOF鏄惁寮�鍚�</div>
                   </td>
                   <td class="el-table__cell is-leaf">
-                    <div class="cell" v-if="cache.info">{{ cache.info.aof_enabled === "0" ? "鍚�" : "鏄�" }}</div>
+                    <div v-if="cache.info" class="cell">{{ cache.info.aof_enabled === '0' ? '鍚�' : '鏄�' }}</div>
                   </td>
                   <td class="el-table__cell is-leaf">
                     <div class="cell">RDB鏄惁鎴愬姛</div>
                   </td>
                   <td class="el-table__cell is-leaf">
-                    <div class="cell" v-if="cache.info">{{ cache.info.rdb_last_bgsave_status }}</div>
+                    <div v-if="cache.info" class="cell">{{ cache.info.rdb_last_bgsave_status }}</div>
                   </td>
                   <td class="el-table__cell is-leaf">
                     <div class="cell">Key鏁伴噺</div>
                   </td>
                   <td class="el-table__cell is-leaf">
-                    <div class="cell" v-if="cache.dbSize">{{ cache.dbSize }}</div>
+                    <div v-if="cache.dbSize" class="cell">{{ cache.dbSize }}</div>
                   </td>
                   <td class="el-table__cell is-leaf">
                     <div class="cell">缃戠粶鍏ュ彛/鍑哄彛</div>
                   </td>
                   <td class="el-table__cell is-leaf">
-                    <div class="cell" v-if="cache.info">
+                    <div v-if="cache.info" class="cell">
                       {{ cache.info.instantaneous_input_kbps }}kps/{{ cache.info.instantaneous_output_kbps }}kps
                     </div>
                   </td>
@@ -100,8 +100,8 @@
       <el-col :span="12" class="card-box">
         <el-card shadow="hover">
           <template #header>
-            <PieChart style="width: 1em; height: 1em; vertical-align: middle;" />
-            <span style="vertical-align: middle;">鍛戒护缁熻</span>
+            <PieChart style="width: 1em; height: 1em; vertical-align: middle" />
+            <span style="vertical-align: middle">鍛戒护缁熻</span>
           </template>
           <div class="el-table el-table--enable-row-hover el-table--medium">
             <div ref="commandstats" style="height: 420px" />
@@ -112,7 +112,7 @@
       <el-col :span="12" class="card-box">
         <el-card shadow="hover">
           <template #header>
-            <Odometer style="width: 1em; height: 1em; vertical-align: middle;" /> <span style="vertical-align: middle;">鍐呭瓨淇℃伅</span>
+            <Odometer style="width: 1em; height: 1em; vertical-align: middle" /> <span style="vertical-align: middle">鍐呭瓨淇℃伅</span>
           </template>
           <div class="el-table el-table--enable-row-hover el-table--medium">
             <div ref="usedmemory" style="height: 420px" />
@@ -126,45 +126,46 @@
 <script setup name="Cache" lang="ts">
 import { getCache } from '@/api/monitor/cache';
 import * as echarts from 'echarts';
+import { CacheVO } from '@/api/monitor/cache/types';
 
-const cache = ref<any>({});
+const cache = ref<Partial<CacheVO>>({});
 const commandstats = ref();
 const usedmemory = ref();
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
 const getList = async () => {
-  proxy?.$modal.loading("姝e湪鍔犺浇缂撳瓨鐩戞帶鏁版嵁锛岃绋嶅�欙紒");
+  proxy?.$modal.loading('姝e湪鍔犺浇缂撳瓨鐩戞帶鏁版嵁锛岃绋嶅�欙紒');
   const res = await getCache();
   proxy?.$modal.closeLoading();
   cache.value = res.data;
-  const commandstatsIntance = echarts.init(commandstats.value, "macarons");
+  const commandstatsIntance = echarts.init(commandstats.value, 'macarons');
   commandstatsIntance.setOption({
     tooltip: {
-      trigger: "item",
-      formatter: "{a} <br/>{b} : {c} ({d}%)"
+      trigger: 'item',
+      formatter: '{a} <br/>{b} : {c} ({d}%)'
     },
     series: [
       {
-        name: "鍛戒护",
-        type: "pie",
-        roseType: "radius",
+        name: '鍛戒护',
+        type: 'pie',
+        roseType: 'radius',
         radius: [15, 95],
-        center: ["50%", "38%"],
+        center: ['50%', '38%'],
         data: res.data.commandStats,
-        animationEasing: "cubicInOut",
+        animationEasing: 'cubicInOut',
         animationDuration: 1000
       }
     ]
   });
-  const usedmemoryInstance = echarts.init(usedmemory.value, "macarons");
+  const usedmemoryInstance = echarts.init(usedmemory.value, 'macarons');
   usedmemoryInstance.setOption({
     tooltip: {
-      formatter: "{b} <br/>{a} : " + cache.value.info.used_memory_human
+      formatter: '{b} <br/>{a} : ' + cache.value.info.used_memory_human
     },
     series: [
       {
-        name: "宄板��",
-        type: "gauge",
+        name: '宄板��',
+        type: 'gauge',
         min: 0,
         max: 1000,
         detail: {
@@ -173,19 +174,19 @@
         data: [
           {
             value: parseFloat(cache.value.info.used_memory_human),
-            name: "鍐呭瓨娑堣��"
+            name: '鍐呭瓨娑堣��'
           }
         ]
       }
     ]
-  })
-  window.addEventListener("resize",()=>{
-    commandstatsIntance.resize()
-    usedmemoryInstance.resize()
   });
-}
+  window.addEventListener('resize', () => {
+    commandstatsIntance.resize();
+    usedmemoryInstance.resize();
+  });
+};
 
 onMounted(() => {
   getList();
-})
+});
 </script>
diff --git a/src/views/monitor/logininfor/index.vue b/src/views/monitor/logininfor/index.vue
index 067790e..27c0a85 100644
--- a/src/views/monitor/logininfor/index.vue
+++ b/src/views/monitor/logininfor/index.vue
@@ -1,17 +1,17 @@
 <template>
   <div class="p-2">
     <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
-      <div class="mb-[10px]" v-show="showSearch">
+      <div v-show="showSearch" class="mb-[10px]">
         <el-card shadow="hover">
-          <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
+          <el-form ref="queryFormRef" :model="queryParams" :inline="true">
             <el-form-item label="鐧诲綍鍦板潃" prop="ipaddr">
-              <el-input v-model="queryParams.ipaddr" placeholder="璇疯緭鍏ョ櫥褰曞湴鍧�" clearable style="width: 240px;" @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.ipaddr" placeholder="璇疯緭鍏ョ櫥褰曞湴鍧�" clearable @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item label="鐢ㄦ埛鍚嶇О" prop="userName">
-              <el-input v-model="queryParams.userName" placeholder="璇疯緭鍏ョ敤鎴峰悕绉�" clearable style="width: 240px;" @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.userName" placeholder="璇疯緭鍏ョ敤鎴峰悕绉�" clearable @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item label="鐘舵��" prop="status">
-              <el-select v-model="queryParams.status" placeholder="鐧诲綍鐘舵��" clearable style="width: 240px">
+              <el-select v-model="queryParams.status" placeholder="鐧诲綍鐘舵��" clearable >
                 <el-option v-for="dict in sys_common_status" :key="dict.value" :label="dict.label" :value="dict.value" />
               </el-select>
             </el-form-item>
@@ -39,22 +39,22 @@
       <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="['monitor:logininfor:remove']">
+            <el-button v-hasPermi="['monitor:logininfor:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">
               鍒犻櫎
             </el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="danger" plain icon="Delete" @click="handleClean" v-hasPermi="['monitor:logininfor:remove']">娓呯┖</el-button>
+            <el-button v-hasPermi="['monitor:logininfor:remove']" type="danger" plain icon="Delete" @click="handleClean">娓呯┖</el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="primary" plain icon="Unlock" :disabled="single" @click="handleUnlock" v-hasPermi="['monitor:logininfor:unlock']">
+            <el-button v-hasPermi="['monitor:logininfor:unlock']" type="primary" plain icon="Unlock" :disabled="single" @click="handleUnlock">
               瑙i攣
             </el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['monitor:logininfor:export']">瀵煎嚭</el-button>
+            <el-button v-hasPermi="['monitor:logininfor:export']" type="warning" plain icon="Download" @click="handleExport">瀵煎嚭</el-button>
           </el-col>
-          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+          <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
         </el-row>
       </template>
 
@@ -62,8 +62,8 @@
         ref="loginInfoTableRef"
         v-loading="loading"
         :data="loginInfoList"
-        @selection-change="handleSelectionChange"
         :default-sort="defaultSort"
+        @selection-change="handleSelectionChange"
         @sort-change="handleSortChange"
       >
         <el-table-column type="selection" width="55" align="center" />
@@ -99,18 +99,18 @@
         </el-table-column>
       </el-table>
 
-      <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+      <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
     </el-card>
   </div>
 </template>
 
 <script setup name="Logininfor" lang="ts">
-import { list, delLoginInfo, cleanLoginInfo, unlockLoginInfo } from "@/api/monitor/loginInfo";
-import { LoginInfoQuery, LoginInfoVO } from "@/api/monitor/loginInfo/types";
+import { list, delLoginInfo, cleanLoginInfo, unlockLoginInfo } from '@/api/monitor/loginInfo';
+import { LoginInfoQuery, LoginInfoVO } from '@/api/monitor/loginInfo/types';
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
-const { sys_device_type } = toRefs<any>(proxy?.useDict("sys_device_type"));
-const { sys_common_status } = toRefs<any>(proxy?.useDict("sys_common_status"));
+const { sys_device_type } = toRefs<any>(proxy?.useDict('sys_device_type'));
+const { sys_common_status } = toRefs<any>(proxy?.useDict('sys_common_status'));
 
 const loginInfoList = ref<LoginInfoVO[]>([]);
 const loading = ref(true);
@@ -120,85 +120,89 @@
 const multiple = ref(true);
 const selectName = ref<Array<string>>([]);
 const total = ref(0);
-const dateRange = ref<[DateModelType,DateModelType]>(['', '']);
-const defaultSort = ref<any>({ prop: "loginTime", order: "descending" });
+const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
+const defaultSort = ref<any>({ prop: 'loginTime', order: 'descending' });
 
 const queryFormRef = ref<ElFormInstance>();
 const loginInfoTableRef = ref<ElTableInstance>();
 // 鏌ヨ鍙傛暟
 const queryParams = ref<LoginInfoQuery>({
-    pageNum: 1,
-    pageSize: 10,
-    ipaddr: '',
-    userName: '',
-    status: '',
-    orderByColumn: defaultSort.value.prop,
-    isAsc: defaultSort.value.order
+  pageNum: 1,
+  pageSize: 10,
+  ipaddr: '',
+  userName: '',
+  status: '',
+  orderByColumn: defaultSort.value.prop,
+  isAsc: defaultSort.value.order
 });
 
 /** 鏌ヨ鐧诲綍鏃ュ織鍒楄〃 */
 const getList = async () => {
-    loading.value = true;
-    const res = await list(proxy?.addDateRange(queryParams.value, dateRange.value));
-    loginInfoList.value = res.rows;
-    total.value = res.total;
-    loading.value = false;
-}
+  loading.value = true;
+  const res = await list(proxy?.addDateRange(queryParams.value, dateRange.value));
+  loginInfoList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+};
 /** 鎼滅储鎸夐挳鎿嶄綔 */
 const handleQuery = () => {
-    queryParams.value.pageNum = 1;
-    getList();
-}
+  queryParams.value.pageNum = 1;
+  getList();
+};
 /** 閲嶇疆鎸夐挳鎿嶄綔 */
 const resetQuery = () => {
-    dateRange.value = ['', ''];
-    queryFormRef.value?.resetFields();
-    queryParams.value.pageNum = 1;
-    loginInfoTableRef.value?.sort(defaultSort.value.prop, defaultSort.value.order);
-}
+  dateRange.value = ['', ''];
+  queryFormRef.value?.resetFields();
+  queryParams.value.pageNum = 1;
+  loginInfoTableRef.value?.sort(defaultSort.value.prop, defaultSort.value.order);
+};
 /** 澶氶�夋閫変腑鏁版嵁 */
 const handleSelectionChange = (selection: LoginInfoVO[]) => {
-    ids.value = selection.map(item => item.infoId);
-    multiple.value = !selection.length;
-    single.value = selection.length != 1;
-    selectName.value = selection.map(item => item.userName);
-}
+  ids.value = selection.map((item) => item.infoId);
+  multiple.value = !selection.length;
+  single.value = selection.length != 1;
+  selectName.value = selection.map((item) => item.userName);
+};
 /** 鎺掑簭瑙﹀彂浜嬩欢 */
 const handleSortChange = (column: any) => {
-    queryParams.value.orderByColumn = column.prop;
-    queryParams.value.isAsc = column.order;
-    getList();
-}
+  queryParams.value.orderByColumn = column.prop;
+  queryParams.value.isAsc = column.order;
+  getList();
+};
 /** 鍒犻櫎鎸夐挳鎿嶄綔 */
 const handleDelete = async (row?: LoginInfoVO) => {
-    const infoIds = row?.infoId || ids.value;
-    await proxy?.$modal.confirm('鏄惁纭鍒犻櫎璁块棶缂栧彿涓�"' + infoIds + '"鐨勬暟鎹」?');
-    await delLoginInfo(infoIds);
-    await getList();
-    proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-}
+  const infoIds = row?.infoId || ids.value;
+  await proxy?.$modal.confirm('鏄惁纭鍒犻櫎璁块棶缂栧彿涓�"' + infoIds + '"鐨勬暟鎹」?');
+  await delLoginInfo(infoIds);
+  await getList();
+  proxy?.$modal.msgSuccess('鍒犻櫎鎴愬姛');
+};
 /** 娓呯┖鎸夐挳鎿嶄綔 */
 const handleClean = async () => {
-    await proxy?.$modal.confirm("鏄惁纭娓呯┖鎵�鏈夌櫥褰曟棩蹇楁暟鎹」?");
-    await cleanLoginInfo();
-    await getList();
-    proxy?.$modal.msgSuccess("娓呯┖鎴愬姛");
-}
+  await proxy?.$modal.confirm('鏄惁纭娓呯┖鎵�鏈夌櫥褰曟棩蹇楁暟鎹」?');
+  await cleanLoginInfo();
+  await getList();
+  proxy?.$modal.msgSuccess('娓呯┖鎴愬姛');
+};
 /** 瑙i攣鎸夐挳鎿嶄綔 */
 const handleUnlock = async () => {
-    const username = selectName.value;
-    await proxy?.$modal.confirm('鏄惁纭瑙i攣鐢ㄦ埛"' + username + '"鏁版嵁椤�?');
-    await unlockLoginInfo(username);
-    proxy?.$modal.msgSuccess("鐢ㄦ埛" + username + "瑙i攣鎴愬姛");
-}
+  const username = selectName.value;
+  await proxy?.$modal.confirm('鏄惁纭瑙i攣鐢ㄦ埛"' + username + '"鏁版嵁椤�?');
+  await unlockLoginInfo(username);
+  proxy?.$modal.msgSuccess('鐢ㄦ埛' + username + '瑙i攣鎴愬姛');
+};
 /** 瀵煎嚭鎸夐挳鎿嶄綔 */
 const handleExport = () => {
-    proxy?.download("monitor/logininfor/export", {
-        ...queryParams.value,
-    }, `config_${new Date().getTime()}.xlsx`);
-}
+  proxy?.download(
+    'monitor/logininfor/export',
+    {
+      ...queryParams.value
+    },
+    `config_${new Date().getTime()}.xlsx`
+  );
+};
 
 onMounted(() => {
-    getList();
-})
+  getList();
+});
 </script>
diff --git a/src/views/monitor/online/index.vue b/src/views/monitor/online/index.vue
index d105cd7..eb12364 100644
--- a/src/views/monitor/online/index.vue
+++ b/src/views/monitor/online/index.vue
@@ -2,12 +2,12 @@
   <div class="p-2">
     <div class="mb-[10px]">
       <el-card shadow="hover">
-        <el-form :model="queryParams" ref="queryFormRef" :inline="true">
+        <el-form ref="queryFormRef" :model="queryParams" :inline="true">
           <el-form-item label="鐧诲綍鍦板潃" prop="ipaddr">
-            <el-input v-model="queryParams.ipaddr" placeholder="璇疯緭鍏ョ櫥褰曞湴鍧�" clearable style="width: 200px" @keyup.enter="handleQuery" />
+            <el-input v-model="queryParams.ipaddr" placeholder="璇疯緭鍏ョ櫥褰曞湴鍧�" clearable @keyup.enter="handleQuery" />
           </el-form-item>
           <el-form-item label="鐢ㄦ埛鍚嶇О" prop="userName">
-            <el-input v-model="queryParams.userName" placeholder="璇疯緭鍏ョ敤鎴峰悕绉�" clearable style="width: 200px" @keyup.enter="handleQuery" />
+            <el-input v-model="queryParams.userName" placeholder="璇疯緭鍏ョ敤鎴峰悕绉�" clearable @keyup.enter="handleQuery" />
           </el-form-item>
           <el-form-item>
             <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
@@ -20,7 +20,7 @@
       <el-table
         v-loading="loading"
         :data="onlineList.slice((queryParams.pageNum - 1) * queryParams.pageSize, queryParams.pageNum * queryParams.pageSize)"
-        style="width: 100%;"
+        style="width: 100%"
       >
         <el-table-column label="搴忓彿" width="50" type="index" align="center">
           <template #default="scope">
@@ -48,26 +48,26 @@
         <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="Delete" @click="handleForceLogout(scope.row)" v-hasPermi="['monitor:online:forceLogout']">
+              <el-button v-hasPermi="['monitor:online:forceLogout']" link type="primary" icon="Delete" @click="handleForceLogout(scope.row)">
               </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 v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" />
     </el-card>
   </div>
 </template>
 
 <script setup name="Online" lang="ts">
-import { forceLogout, list as initData } from "@/api/monitor/online";
-import { OnlineQuery, OnlineVO } from "@/api/monitor/online/types";
-import api from "@/api/system/user";
-import {to} from "await-to-js";
+import { forceLogout, list as initData } from '@/api/monitor/online';
+import { OnlineQuery, OnlineVO } from '@/api/monitor/online/types';
+import api from '@/api/system/user';
+import { to } from 'await-to-js';
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
-const { sys_device_type } = toRefs<any>(proxy?.useDict("sys_device_type"));
+const { sys_device_type } = toRefs<any>(proxy?.useDict('sys_device_type'));
 
 const onlineList = ref<OnlineVO[]>([]);
 const loading = ref(true);
@@ -89,28 +89,28 @@
   onlineList.value = res.rows;
   total.value = res.total;
   loading.value = false;
-}
+};
 /** 鎼滅储鎸夐挳鎿嶄綔 */
 const handleQuery = () => {
   queryParams.value.pageNum = 1;
   getList();
-}
+};
 /** 閲嶇疆鎸夐挳鎿嶄綔 */
 const resetQuery = () => {
   queryFormRef.value?.resetFields();
   handleQuery();
-}
+};
 /** 寮洪��鎸夐挳鎿嶄綔 */
 const handleForceLogout = async (row: OnlineVO) => {
   const [err] = await to(proxy?.$modal.confirm('鏄惁纭寮洪��鍚嶇О涓�"' + row.userName + '"鐨勭敤鎴�?') as any);
   if (!err) {
     await forceLogout(row.tokenId);
     await getList();
-    proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+    proxy?.$modal.msgSuccess('鍒犻櫎鎴愬姛');
   }
-}
+};
 
 onMounted(() => {
   getList();
-})
+});
 </script>
diff --git a/src/views/monitor/operlog/index.vue b/src/views/monitor/operlog/index.vue
index ea68c24..1442984 100644
--- a/src/views/monitor/operlog/index.vue
+++ b/src/views/monitor/operlog/index.vue
@@ -1,25 +1,25 @@
 <template>
   <div class="p-2">
     <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
-      <div class="mb-[10px]">
+      <div v-show="showSearch" class="mb-[10px]">
         <el-card shadow="hover">
-          <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
+          <el-form ref="queryFormRef" :model="queryParams" :inline="true">
             <el-form-item label="鎿嶄綔鍦板潃" prop="operIp">
-              <el-input v-model="queryParams.operIp" placeholder="璇疯緭鍏ユ搷浣滃湴鍧�" clearable style="width: 240px;" @keyup.enter="handleQuery"/>
+              <el-input v-model="queryParams.operIp" placeholder="璇疯緭鍏ユ搷浣滃湴鍧�" clearable @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item label="绯荤粺妯″潡" prop="title">
-              <el-input v-model="queryParams.title" placeholder="璇疯緭鍏ョ郴缁熸ā鍧�" clearable style="width: 240px;" @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.title" placeholder="璇疯緭鍏ョ郴缁熸ā鍧�" clearable @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item label="鎿嶄綔浜哄憳" prop="operName">
-              <el-input v-model="queryParams.operName" placeholder="璇疯緭鍏ユ搷浣滀汉鍛�" clearable style="width: 240px;" @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.operName" placeholder="璇疯緭鍏ユ搷浣滀汉鍛�" clearable @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item label="绫诲瀷" prop="businessType">
-              <el-select v-model="queryParams.businessType" placeholder="鎿嶄綔绫诲瀷" clearable style="width: 240px">
+              <el-select v-model="queryParams.businessType" placeholder="鎿嶄綔绫诲瀷" clearable >
                 <el-option v-for="dict in sys_oper_type" :key="dict.value" :label="dict.label" :value="dict.value" />
               </el-select>
             </el-form-item>
             <el-form-item label="鐘舵��" prop="status">
-              <el-select v-model="queryParams.status" placeholder="鎿嶄綔鐘舵��" clearable style="width: 240px">
+              <el-select v-model="queryParams.status" placeholder="鎿嶄綔鐘舵��" clearable >
                 <el-option v-for="dict in sys_common_status" :key="dict.value" :label="dict.label" :value="dict.value" />
               </el-select>
             </el-form-item>
@@ -47,17 +47,17 @@
       <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="['monitor:operlog:remove']">
+            <el-button v-hasPermi="['monitor:operlog:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">
               鍒犻櫎
             </el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="danger" plain icon="WarnTriangleFilled" @click="handleClean" v-hasPermi="['monitor:operlog:remove']">娓呯┖</el-button>
+            <el-button v-hasPermi="['monitor:operlog:remove']" type="danger" plain icon="WarnTriangleFilled" @click="handleClean">娓呯┖</el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['monitor:operlog:export']">瀵煎嚭</el-button>
+            <el-button v-hasPermi="['monitor:operlog:export']" type="warning" plain icon="Download" @click="handleExport">瀵煎嚭</el-button>
           </el-col>
-          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+          <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
         </el-row>
       </template>
 
@@ -65,8 +65,8 @@
         ref="operLogTableRef"
         v-loading="loading"
         :data="operlogList"
-        @selection-change="handleSelectionChange"
         :default-sort="defaultSort"
+        @selection-change="handleSelectionChange"
         @sort-change="handleSortChange"
       >
         <el-table-column type="selection" width="50" align="center" />
@@ -114,20 +114,20 @@
         <el-table-column label="鎿嶄綔" fixed="right" align="center" class-name="small-padding fixed-width">
           <template #default="scope">
             <el-tooltip content="璇︾粏" placement="top">
-              <el-button link type="primary" icon="View" @click="handleView(scope.row)" v-hasPermi="['monitor:operlog:query']"> </el-button>
+              <el-button v-hasPermi="['monitor:operlog:query']" link type="primary" icon="View" @click="handleView(scope.row)"> </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" />
+      <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
     </el-card>
     <!-- 鎿嶄綔鏃ュ織璇︾粏 -->
-    <el-dialog title="鎿嶄綔鏃ュ織璇︾粏" v-model="dialog.visible" width="700px" append-to-body>
+    <el-dialog v-model="dialog.visible" title="鎿嶄綔鏃ュ織璇︾粏" width="700px" append-to-body>
       <el-form :model="form" label-width="100px">
         <el-row>
           <el-col :span="24">
-            <el-form-item label="鐧诲綍淇℃伅锛�">{{ form.operName }} / {{form.deptName}} / {{ form.operIp }} / {{ form.operLocation }}</el-form-item>
+            <el-form-item label="鐧诲綍淇℃伅锛�">{{ form.operName }} / {{ form.deptName }} / {{ form.operIp }} / {{ form.operLocation }}</el-form-item>
           </el-col>
           <el-col :span="12">
             <el-form-item label="璇锋眰淇℃伅锛�">{{ form.requestMethod }} {{ form.operUrl }}</el-form-item>
@@ -157,7 +157,7 @@
             <el-form-item label="鎿嶄綔鏃堕棿锛�">{{ parseTime(form.operTime) }}</el-form-item>
           </el-col>
           <el-col :span="24">
-            <el-form-item label="寮傚父淇℃伅锛�" v-if="form.status === 1">{{ form.errorMsg }}</el-form-item>
+            <el-form-item v-if="form.status === 1" label="寮傚父淇℃伅锛�">{{ form.errorMsg }}</el-form-item>
           </el-col>
         </el-row>
       </el-form>
@@ -175,7 +175,7 @@
 import { OperLogForm, OperLogQuery, OperLogVO } from '@/api/monitor/operlog/types';
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
-const { sys_oper_type, sys_common_status } = toRefs<any>(proxy?.useDict("sys_oper_type", "sys_common_status"));
+const { sys_oper_type, sys_common_status } = toRefs<any>(proxy?.useDict('sys_oper_type', 'sys_common_status'));
 
 const operlogList = ref<OperLogVO[]>([]);
 const loading = ref(true);
@@ -184,7 +184,7 @@
 const multiple = ref(true);
 const total = ref(0);
 const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
-const defaultSort = ref<any>({ prop: "operTime", order: "descending" });
+const defaultSort = ref<any>({ prop: 'operTime', order: 'descending' });
 
 const operLogTableRef = ref<ElTableInstance>();
 const queryFormRef = ref<ElFormInstance>();
@@ -193,7 +193,6 @@
   visible: false,
   title: ''
 });
-
 
 const data = reactive<PageData<OperLogForm, OperLogQuery>>({
   form: {
@@ -240,63 +239,67 @@
   operlogList.value = res.rows;
   total.value = res.total;
   loading.value = false;
-}
+};
 /** 鎿嶄綔鏃ュ織绫诲瀷瀛楀吀缈昏瘧 */
 const typeFormat = (row: OperLogForm) => {
   return proxy?.selectDictLabel(sys_oper_type.value, row.businessType);
-}
+};
 /** 鎼滅储鎸夐挳鎿嶄綔 */
 const handleQuery = () => {
   queryParams.value.pageNum = 1;
   getList();
-}
+};
 /** 閲嶇疆鎸夐挳鎿嶄綔 */
 const resetQuery = () => {
   dateRange.value = ['', ''];
   queryFormRef.value?.resetFields();
   queryParams.value.pageNum = 1;
   operLogTableRef.value?.sort(defaultSort.value.prop, defaultSort.value.order);
-}
+};
 /** 澶氶�夋閫変腑鏁版嵁 */
 const handleSelectionChange = (selection: OperLogVO[]) => {
-  ids.value = selection.map(item => item.operId);
+  ids.value = selection.map((item) => item.operId);
   multiple.value = !selection.length;
-}
+};
 /** 鎺掑簭瑙﹀彂浜嬩欢 */
 const handleSortChange = (column: any) => {
   queryParams.value.orderByColumn = column.prop;
   queryParams.value.isAsc = column.order;
   getList();
-}
+};
 /** 璇︾粏鎸夐挳鎿嶄綔 */
 const handleView = (row: OperLogVO) => {
   dialog.visible = true;
   form.value = row;
-}
+};
 /** 鍒犻櫎鎸夐挳鎿嶄綔 */
 const handleDelete = async (row?: OperLogVO) => {
   const operIds = row?.operId || ids.value;
   await proxy?.$modal.confirm('鏄惁纭鍒犻櫎鏃ュ織缂栧彿涓�"' + operIds + '"鐨勬暟鎹」?');
   await delOperlog(operIds);
   await getList();
-  proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-}
+  proxy?.$modal.msgSuccess('鍒犻櫎鎴愬姛');
+};
 
 /** 娓呯┖鎸夐挳鎿嶄綔 */
 const handleClean = async () => {
-  await proxy?.$modal.confirm("鏄惁纭娓呯┖鎵�鏈夋搷浣滄棩蹇楁暟鎹」?");
+  await proxy?.$modal.confirm('鏄惁纭娓呯┖鎵�鏈夋搷浣滄棩蹇楁暟鎹」?');
   await cleanOperlog();
   await getList();
-  proxy?.$modal.msgSuccess("娓呯┖鎴愬姛");
-}
+  proxy?.$modal.msgSuccess('娓呯┖鎴愬姛');
+};
 
 /** 瀵煎嚭鎸夐挳鎿嶄綔 */
 const handleExport = () => {
-  proxy?.download("monitor/operlog/export", {
-    ...queryParams.value,
-  }, `config_${new Date().getTime()}.xlsx`);
-}
+  proxy?.download(
+    'monitor/operlog/export',
+    {
+      ...queryParams.value
+    },
+    `config_${new Date().getTime()}.xlsx`
+  );
+};
 onMounted(() => {
   getList();
-})
+});
 </script>
diff --git a/src/views/monitor/powerjob/index.vue b/src/views/monitor/snailjob/index.vue
similarity index 67%
rename from src/views/monitor/powerjob/index.vue
rename to src/views/monitor/snailjob/index.vue
index 0319e97..89e3b23 100644
--- a/src/views/monitor/powerjob/index.vue
+++ b/src/views/monitor/snailjob/index.vue
@@ -5,5 +5,5 @@
 </template>
 
 <script setup lang="ts">
-const url = ref(import.meta.env.VITE_APP_POWERJOB_ADMIN);
+const url = ref(import.meta.env.VITE_APP_SNAILJOB_ADMIN);
 </script>
diff --git a/src/views/redirect/index.vue b/src/views/redirect/index.vue
index 15401f8..97a6556 100644
--- a/src/views/redirect/index.vue
+++ b/src/views/redirect/index.vue
@@ -3,12 +3,12 @@
 </template>
 
 <script setup>
-import { useRoute, useRouter } from 'vue-router'
+import { useRoute, useRouter } from 'vue-router';
 
 const route = useRoute();
 const router = useRouter();
-const { params, query } = route
-const { path } = params
+const { params, query } = route;
+const { path } = params;
 
-router.replace({ path: '/' + path, query })
+router.replace({ path: '/' + path, query });
 </script>
diff --git a/src/views/register.vue b/src/views/register.vue
index ad78cf1..c3d1930 100644
--- a/src/views/register.vue
+++ b/src/views/register.vue
@@ -2,7 +2,7 @@
   <div class="register">
     <el-form ref="registerRef" :model="registerForm" :rules="registerRules" class="register-form">
       <h3 class="title">RuoYi-Vue-Plus澶氱鎴风鐞嗙郴缁�</h3>
-      <el-form-item prop="tenantId" v-if="tenantEnabled">
+      <el-form-item v-if="tenantEnabled" prop="tenantId">
         <el-select v-model="registerForm.tenantId" filterable placeholder="璇烽�夋嫨/杈撳叆鍏徃鍚嶇О" style="width: 100%">
           <el-option v-for="item in tenantList" :key="item.tenantId" :label="item.companyName" :value="item.tenantId"> </el-option>
           <template #prefix><svg-icon icon-class="company" class="el-input__icon input-icon" /></template>
@@ -30,27 +30,27 @@
           <template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template>
         </el-input>
       </el-form-item>
-      <el-form-item prop="code" v-if="captchaEnabled">
-        <el-input size="large" v-model="registerForm.code" auto-complete="off" placeholder="楠岃瘉鐮�" style="width: 63%" @keyup.enter="handleRegister">
+      <el-form-item v-if="captchaEnabled" prop="code">
+        <el-input v-model="registerForm.code" size="large" auto-complete="off" placeholder="楠岃瘉鐮�" style="width: 63%" @keyup.enter="handleRegister">
           <template #prefix><svg-icon icon-class="validCode" class="el-input__icon input-icon" /></template>
         </el-input>
         <div class="register-code">
-          <img :src="codeUrl" @click="getCode" class="register-code-img" />
+          <img :src="codeUrl" class="register-code-img" @click="getCode" />
         </div>
       </el-form-item>
-      <el-form-item style="width:100%;">
-        <el-button :loading="loading" size="large" type="primary" style="width:100%;" @click.prevent="handleRegister">
+      <el-form-item style="width: 100%">
+        <el-button :loading="loading" size="large" type="primary" style="width: 100%" @click.prevent="handleRegister">
           <span v-if="!loading">娉� 鍐�</span>
           <span v-else>娉� 鍐� 涓�...</span>
         </el-button>
-        <div style="float: right;">
+        <div style="float: right">
           <router-link class="link-type" :to="'/login'">浣跨敤宸叉湁璐︽埛鐧诲綍</router-link>
         </div>
       </el-form-item>
     </el-form>
     <!--  搴曢儴  -->
     <div class="el-register-footer">
-      <span>Copyright 漏 2018-2023 鐤媯鐨勭嫯瀛怢i All Rights Reserved.</span>
+      <span>Copyright 漏 2018-2024 鐤媯鐨勭嫯瀛怢i All Rights Reserved.</span>
     </div>
   </div>
 </template>
@@ -63,46 +63,44 @@
 const router = useRouter();
 
 const registerForm = ref<RegisterForm>({
-  tenantId: "",
-  username: "",
-  password: "",
-  confirmPassword: "",
-  code: "",
-  uuid: "",
-  userType: "sys_user"
+  tenantId: '',
+  username: '',
+  password: '',
+  confirmPassword: '',
+  code: '',
+  uuid: '',
+  userType: 'sys_user'
 });
 
 // 绉熸埛寮�鍏�
 const tenantEnabled = ref(true);
 
-
 const equalToPassword = (rule: any, value: string, callback: any) => {
   if (registerForm.value.password !== value) {
-    callback(new Error("涓ゆ杈撳叆鐨勫瘑鐮佷笉涓�鑷�"));
+    callback(new Error('涓ゆ杈撳叆鐨勫瘑鐮佷笉涓�鑷�'));
   } else {
     callback();
   }
 };
 
 const registerRules: ElFormRules = {
-  tenantId: [
-    { required: true, trigger: "blur", message: "璇疯緭鍏ユ偍鐨勭鎴风紪鍙�" }
-  ],
+  tenantId: [{ required: true, trigger: 'blur', message: '璇疯緭鍏ユ偍鐨勭鎴风紪鍙�' }],
   username: [
-    { required: true, trigger: "blur", message: "璇疯緭鍏ユ偍鐨勮处鍙�" },
-    { min: 2, max: 20, message: "鐢ㄦ埛璐﹀彿闀垮害蹇呴』浠嬩簬 2 鍜� 20 涔嬮棿", trigger: "blur" }
+    { required: true, trigger: 'blur', message: '璇疯緭鍏ユ偍鐨勮处鍙�' },
+    { min: 2, max: 20, message: '鐢ㄦ埛璐﹀彿闀垮害蹇呴』浠嬩簬 2 鍜� 20 涔嬮棿', trigger: 'blur' }
   ],
   password: [
-    { required: true, trigger: "blur", message: "璇疯緭鍏ユ偍鐨勫瘑鐮�" },
-    { min: 5, max: 20, message: "鐢ㄦ埛瀵嗙爜闀垮害蹇呴』浠嬩簬 5 鍜� 20 涔嬮棿", trigger: "blur" }
+    { required: true, trigger: 'blur', message: '璇疯緭鍏ユ偍鐨勫瘑鐮�' },
+    { min: 5, max: 20, message: '鐢ㄦ埛瀵嗙爜闀垮害蹇呴』浠嬩簬 5 鍜� 20 涔嬮棿', trigger: 'blur' },
+    { pattern: /^[^<>"'|\\]+$/, message: '涓嶈兘鍖呭惈闈炴硶瀛楃锛�< > " \' \\\ |', trigger: 'blur' }
   ],
   confirmPassword: [
-    { required: true, trigger: "blur", message: "璇峰啀娆¤緭鍏ユ偍鐨勫瘑鐮�" },
-    { required: true, validator: equalToPassword, trigger: "blur" }
+    { required: true, trigger: 'blur', message: '璇峰啀娆¤緭鍏ユ偍鐨勫瘑鐮�' },
+    { required: true, validator: equalToPassword, trigger: 'blur' }
   ],
-  code: [{ required: true, trigger: "change", message: "璇疯緭鍏ラ獙璇佺爜" }]
+  code: [{ required: true, trigger: 'change', message: '璇疯緭鍏ラ獙璇佺爜' }]
 };
-const codeUrl = ref("");
+const codeUrl = ref('');
 const loading = ref(false);
 const captchaEnabled = ref(true);
 const registerRef = ref<ElFormInstance>();
@@ -116,20 +114,20 @@
       const [err] = await to(register(registerForm.value));
       if (!err) {
         const username = registerForm.value.username;
-        await ElMessageBox.alert("<font color='red'>鎭枩浣狅紝鎮ㄧ殑璐﹀彿 " + username + " 娉ㄥ唽鎴愬姛锛�</font>", "绯荤粺鎻愮ず", {
+        await ElMessageBox.alert("<font color='red'>鎭枩浣狅紝鎮ㄧ殑璐﹀彿 " + username + ' 娉ㄥ唽鎴愬姛锛�</font>', '绯荤粺鎻愮ず', {
           dangerouslyUseHTMLString: true,
-          type: "success",
+          type: 'success'
         });
-        await router.push("/login");
+        await router.push('/login');
       } else {
         loading.value = false;
-        if (captchaEnabled) {
+        if (captchaEnabled.value) {
           getCode();
         }
       }
     }
   });
-}
+};
 
 const getCode = async () => {
   const res = await getCodeImg();
@@ -150,12 +148,12 @@
       registerForm.value.tenantId = tenantList.value[0].tenantId;
     }
   }
-}
+};
 
 onMounted(() => {
   getCode();
   initTenantList();
-})
+});
 </script>
 
 <style lang="scss" scoped>
@@ -164,7 +162,7 @@
   justify-content: center;
   align-items: center;
   height: 100%;
-  background-image: url("../assets/images/login-background.jpg");
+  background-image: url('../assets/images/login-background.jpg');
   background-size: cover;
 }
 
diff --git a/src/views/system/client/index.vue b/src/views/system/client/index.vue
index b39b9e5..af8696b 100644
--- a/src/views/system/client/index.vue
+++ b/src/views/system/client/index.vue
@@ -1,16 +1,16 @@
 <template>
   <div class="p-2">
     <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
-      <div class="search" v-show="showSearch">
-        <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="100px">
+      <div v-show="showSearch" class="search">
+        <el-form ref="queryFormRef" :model="queryParams" :inline="true">
           <el-form-item label="瀹㈡埛绔痥ey" prop="clientKey">
-            <el-input v-model="queryParams.clientKey" placeholder="璇疯緭鍏ュ鎴风key" clearable style="width: 240px" @keyup.enter="handleQuery" />
+            <el-input v-model="queryParams.clientKey" placeholder="璇疯緭鍏ュ鎴风key" clearable @keyup.enter="handleQuery" />
           </el-form-item>
           <el-form-item label="瀹㈡埛绔閽�" prop="clientSecret">
-            <el-input v-model="queryParams.clientSecret" placeholder="璇疯緭鍏ュ鎴风绉橀挜" clearable style="width: 240px" @keyup.enter="handleQuery" />
+            <el-input v-model="queryParams.clientSecret" placeholder="璇疯緭鍏ュ鎴风绉橀挜" clearable @keyup.enter="handleQuery" />
           </el-form-item>
           <el-form-item label="鐘舵��" prop="status">
-            <el-select v-model="queryParams.status" placeholder="鐘舵��" clearable>
+            <el-select v-model="queryParams.status" placeholder="鐘舵��" clearable >
               <el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
             </el-select>
           </el-form-item>
@@ -26,28 +26,28 @@
       <template #header>
         <el-row :gutter="10" class="mb8">
           <el-col :span="1.5">
-            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:client:add']">鏂板</el-button>
+            <el-button v-hasPermi="['system:client:add']" type="primary" plain icon="Plus" @click="handleAdd">鏂板</el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:client:edit']">
+            <el-button v-hasPermi="['system:client:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()">
               淇敼
             </el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:client:remove']">
+            <el-button v-hasPermi="['system:client:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">
               鍒犻櫎
             </el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:client:export']">瀵煎嚭</el-button>
+            <el-button v-hasPermi="['system:client:export']" type="warning" plain icon="Download" @click="handleExport">瀵煎嚭</el-button>
           </el-col>
-          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+          <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
         </el-row>
       </template>
 
       <el-table v-loading="loading" :data="clientList" @selection-change="handleSelectionChange">
         <el-table-column type="selection" width="55" align="center" />
-        <el-table-column label="id" align="center" prop="id" v-if="true" />
+        <el-table-column v-if="true" label="id" align="center" prop="id" />
         <el-table-column label="瀹㈡埛绔痠d" align="center" prop="clientId" />
         <el-table-column label="瀹㈡埛绔痥ey" align="center" prop="clientKey" />
         <el-table-column label="瀹㈡埛绔閽�" align="center" prop="clientSecret" />
@@ -63,7 +63,7 @@
         </el-table-column>
         <el-table-column label="Token娲昏穬瓒呮椂鏃堕棿" align="center" prop="activeTimeout" />
         <el-table-column label="Token鍥哄畾瓒呮椂鏃堕棿" align="center" prop="timeout" />
-        <el-table-column label="鐘舵��" align="center" key="status">
+        <el-table-column key="status" label="鐘舵��" align="center">
           <template #default="scope">
             <el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch>
           </template>
@@ -71,19 +71,19 @@
         <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="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:client:edit']"></el-button>
+              <el-button v-hasPermi="['system:client:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
             </el-tooltip>
             <el-tooltip content="鍒犻櫎" placement="top">
-              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:client:remove']"></el-button>
+              <el-button v-hasPermi="['system:client:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></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" />
+      <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
     </el-card>
     <!-- 娣诲姞鎴栦慨鏀瑰鎴风绠$悊瀵硅瘽妗� -->
-    <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+    <el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
       <el-form ref="clientFormRef" :model="form" :rules="rules" label-width="100px">
         <el-form-item label="瀹㈡埛绔痥ey" prop="clientKey">
           <el-input v-model="form.clientKey" :disabled="form.id != null" placeholder="璇疯緭鍏ュ鎴风key" />
@@ -125,7 +125,7 @@
         </el-form-item>
         <el-form-item label="鐘舵��">
           <el-radio-group v-model="form.status">
-            <el-radio v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.value">
+            <el-radio v-for="dict in sys_normal_disable" :key="dict.value" :value="dict.value">
               {{ dict.label }}
             </el-radio>
           </el-radio-group>
@@ -146,9 +146,9 @@
 import { ClientVO, ClientQuery, ClientForm } from '@/api/system/client/types';
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
-const { sys_normal_disable } = toRefs<any>(proxy?.useDict("sys_normal_disable"));
-const { sys_grant_type } = toRefs<any>(proxy?.useDict("sys_grant_type"));
-const { sys_device_type } = toRefs<any>(proxy?.useDict("sys_device_type"));
+const { sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_normal_disable'));
+const { sys_grant_type } = toRefs<any>(proxy?.useDict('sys_grant_type'));
+const { sys_device_type } = toRefs<any>(proxy?.useDict('sys_device_type'));
 
 const clientList = ref<ClientVO[]>([]);
 const buttonLoading = ref(false);
@@ -176,10 +176,10 @@
   deviceType: undefined,
   activeTimeout: undefined,
   timeout: undefined,
-  status: undefined,
-}
+  status: undefined
+};
 const data = reactive<PageData<ClientForm, ClientQuery>>({
-  form: {...initFormData},
+  form: { ...initFormData },
   queryParams: {
     pageNum: 1,
     pageSize: 10,
@@ -190,27 +190,15 @@
     deviceType: undefined,
     activeTimeout: undefined,
     timeout: undefined,
-    status: undefined,
+    status: undefined
   },
   rules: {
-    id: [
-      { required: true, message: "id涓嶈兘涓虹┖", trigger: "blur" }
-    ],
-    clientId: [
-      { required: true, message: "瀹㈡埛绔痠d涓嶈兘涓虹┖", trigger: "blur" }
-    ],
-    clientKey: [
-      { required: true, message: "瀹㈡埛绔痥ey涓嶈兘涓虹┖", trigger: "blur" }
-    ],
-    clientSecret: [
-      { required: true, message: "瀹㈡埛绔閽ヤ笉鑳戒负绌�", trigger: "blur" }
-    ],
-    grantTypeList: [
-      { required: true, message: "鎺堟潈绫诲瀷涓嶈兘涓虹┖", trigger: "change" }
-    ],
-    deviceType: [
-      { required: true, message: "璁惧绫诲瀷涓嶈兘涓虹┖", trigger: "change" }
-    ],
+    id: [{ required: true, message: 'id涓嶈兘涓虹┖', trigger: 'blur' }],
+    clientId: [{ required: true, message: '瀹㈡埛绔痠d涓嶈兘涓虹┖', trigger: 'blur' }],
+    clientKey: [{ required: true, message: '瀹㈡埛绔痥ey涓嶈兘涓虹┖', trigger: 'blur' }],
+    clientSecret: [{ required: true, message: '瀹㈡埛绔閽ヤ笉鑳戒负绌�', trigger: 'blur' }],
+    grantTypeList: [{ required: true, message: '鎺堟潈绫诲瀷涓嶈兘涓虹┖', trigger: 'change' }],
+    deviceType: [{ required: true, message: '璁惧绫诲瀷涓嶈兘涓虹┖', trigger: 'change' }]
   }
 });
 
@@ -223,55 +211,55 @@
   clientList.value = res.rows;
   total.value = res.total;
   loading.value = false;
-}
+};
 
 /** 鍙栨秷鎸夐挳 */
 const cancel = () => {
   reset();
   dialog.visible = false;
-}
+};
 
 /** 琛ㄥ崟閲嶇疆 */
 const reset = () => {
-  form.value = {...initFormData};
+  form.value = { ...initFormData };
   clientFormRef.value?.resetFields();
-}
+};
 
 /** 鎼滅储鎸夐挳鎿嶄綔 */
 const handleQuery = () => {
   queryParams.value.pageNum = 1;
   getList();
-}
+};
 
 /** 閲嶇疆鎸夐挳鎿嶄綔 */
 const resetQuery = () => {
   queryFormRef.value?.resetFields();
   handleQuery();
-}
+};
 
 /** 澶氶�夋閫変腑鏁版嵁 */
 const handleSelectionChange = (selection: ClientVO[]) => {
-  ids.value = selection.map(item => item.id);
+  ids.value = selection.map((item) => item.id);
   single.value = selection.length != 1;
   multiple.value = !selection.length;
-}
+};
 
 /** 鏂板鎸夐挳鎿嶄綔 */
 const handleAdd = () => {
   reset();
   dialog.visible = true;
-  dialog.title = "娣诲姞瀹㈡埛绔鐞�";
-}
+  dialog.title = '娣诲姞瀹㈡埛绔鐞�';
+};
 
 /** 淇敼鎸夐挳鎿嶄綔 */
 const handleUpdate = async (row?: ClientVO) => {
   reset();
-  const _id = row?.id || ids.value[0]
+  const _id = row?.id || ids.value[0];
   const res = await getClient(_id);
   Object.assign(form.value, res.data);
   dialog.visible = true;
-  dialog.title = "淇敼瀹㈡埛绔鐞�";
-}
+  dialog.title = '淇敼瀹㈡埛绔鐞�';
+};
 
 /** 鎻愪氦鎸夐挳 */
 const submitForm = () => {
@@ -279,44 +267,48 @@
     if (valid) {
       buttonLoading.value = true;
       if (form.value.id) {
-        await updateClient(form.value).finally(() =>  buttonLoading.value = false);
+        await updateClient(form.value).finally(() => (buttonLoading.value = false));
       } else {
-        await addClient(form.value).finally(() =>  buttonLoading.value = false);
+        await addClient(form.value).finally(() => (buttonLoading.value = false));
       }
-      proxy?.$modal.msgSuccess("淇敼鎴愬姛");
+      proxy?.$modal.msgSuccess('淇敼鎴愬姛');
       dialog.visible = false;
       await getList();
     }
   });
-}
+};
 
 /** 鍒犻櫎鎸夐挳鎿嶄綔 */
 const handleDelete = async (row?: ClientVO) => {
   const _ids = row?.id || ids.value;
-  await proxy?.$modal.confirm('鏄惁纭鍒犻櫎瀹㈡埛绔鐞嗙紪鍙蜂负"' + _ids + '"鐨勬暟鎹」锛�').finally(() => loading.value = false);
+  await proxy?.$modal.confirm('鏄惁纭鍒犻櫎瀹㈡埛绔鐞嗙紪鍙蜂负"' + _ids + '"鐨勬暟鎹」锛�').finally(() => (loading.value = false));
   await delClient(_ids);
-  proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+  proxy?.$modal.msgSuccess('鍒犻櫎鎴愬姛');
   await getList();
-}
+};
 
 /** 瀵煎嚭鎸夐挳鎿嶄綔 */
 const handleExport = () => {
-  proxy?.download('system/client/export', {
-    ...queryParams.value
-  }, `client_${new Date().getTime()}.xlsx`)
-}
+  proxy?.download(
+    'system/client/export',
+    {
+      ...queryParams.value
+    },
+    `client_${new Date().getTime()}.xlsx`
+  );
+};
 
 /** 鐘舵�佷慨鏀�  */
 const handleStatusChange = async (row: ClientVO) => {
-  let text = row.status === "0" ? "鍚敤" : "鍋滅敤"
+  let text = row.status === '0' ? '鍚敤' : '鍋滅敤';
   try {
     await proxy?.$modal.confirm('纭瑕�"' + text + '"鍚�?');
-    await changeStatus(row.id, row.status);
-    proxy?.$modal.msgSuccess(text + "鎴愬姛");
+    await changeStatus(row.clientId, row.status);
+    proxy?.$modal.msgSuccess(text + '鎴愬姛');
   } catch (err) {
-    row.status = row.status === "0" ? "1" : "0";
+    row.status = row.status === '0' ? '1' : '0';
   }
-}
+};
 
 onMounted(() => {
   getList();
diff --git a/src/views/system/config/index.vue b/src/views/system/config/index.vue
index 7e2e161..138068e 100644
--- a/src/views/system/config/index.vue
+++ b/src/views/system/config/index.vue
@@ -1,21 +1,21 @@
 <template>
   <div class="p-2">
     <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
-      <div class="mb-[10px]" v-show="showSearch">
+      <div v-show="showSearch" class="mb-[10px]">
         <el-card shadow="hover">
-          <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
+          <el-form ref="queryFormRef" :model="queryParams" :inline="true">
             <el-form-item label="鍙傛暟鍚嶇О" prop="configName">
-              <el-input v-model="queryParams.configName" placeholder="璇疯緭鍏ュ弬鏁板悕绉�" clearable style="width: 240px" @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.configName" placeholder="璇疯緭鍏ュ弬鏁板悕绉�" clearable @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item label="鍙傛暟閿悕" prop="configKey">
-              <el-input v-model="queryParams.configKey" placeholder="璇疯緭鍏ュ弬鏁伴敭鍚�" clearable style="width: 240px" @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.configKey" placeholder="璇疯緭鍏ュ弬鏁伴敭鍚�" clearable @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item label="绯荤粺鍐呯疆" prop="configType">
-              <el-select v-model="queryParams.configType" placeholder="绯荤粺鍐呯疆" clearable>
+              <el-select v-model="queryParams.configType" placeholder="绯荤粺鍐呯疆" clearable >
                 <el-option v-for="dict in sys_yes_no" :key="dict.value" :label="dict.label" :value="dict.value" />
               </el-select>
             </el-form-item>
-            <el-form-item label="鍒涘缓鏃堕棿" style="width: 308px;">
+            <el-form-item label="鍒涘缓鏃堕棿" style="width: 308px">
               <el-date-picker
                 v-model="dateRange"
                 value-format="YYYY-MM-DD HH:mm:ss"
@@ -38,31 +38,31 @@
       <template #header>
         <el-row :gutter="10" class="mb8">
           <el-col :span="1.5">
-            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:config:add']">鏂板</el-button>
+            <el-button v-hasPermi="['system:config:add']" type="primary" plain icon="Plus" @click="handleAdd">鏂板</el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:config:edit']">
+            <el-button v-hasPermi="['system:config:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()">
               淇敼
             </el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:config:remove']">
+            <el-button v-hasPermi="['system:config:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">
               鍒犻櫎
             </el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:config:export']">瀵煎嚭</el-button>
+            <el-button v-hasPermi="['system:config:export']" type="warning" plain icon="Download" @click="handleExport">瀵煎嚭</el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="danger" plain icon="Refresh" @click="handleRefreshCache" v-hasPermi="['system:config:remove']">鍒锋柊缂撳瓨</el-button>
+            <el-button v-hasPermi="['system:config:remove']" type="danger" plain icon="Refresh" @click="handleRefreshCache">鍒锋柊缂撳瓨</el-button>
           </el-col>
-          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+          <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
         </el-row>
       </template>
 
       <el-table v-loading="loading" :data="configList" @selection-change="handleSelectionChange">
         <el-table-column type="selection" width="55" align="center" />
-        <el-table-column label="鍙傛暟涓婚敭" align="center" prop="configId" v-if="false" />
+        <el-table-column v-if="false" label="鍙傛暟涓婚敭" align="center" prop="configId" />
         <el-table-column label="鍙傛暟鍚嶇О" align="center" prop="configName" :show-overflow-tooltip="true" />
         <el-table-column label="鍙傛暟閿悕" align="center" prop="configKey" :show-overflow-tooltip="true" />
         <el-table-column label="鍙傛暟閿��" align="center" prop="configValue" :show-overflow-tooltip="true" />
@@ -80,19 +80,19 @@
         <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="['system:config:edit']"></el-button>
+              <el-button v-hasPermi="['system:config:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
             </el-tooltip>
             <el-tooltip content="鍒犻櫎" placement="top">
-              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:config:remove']"></el-button>
+              <el-button v-hasPermi="['system:config:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></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" />
+      <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
     </el-card>
 
     <!-- 娣诲姞鎴栦慨鏀瑰弬鏁伴厤缃璇濇 -->
-    <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+    <el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
       <el-form ref="configFormRef" :model="form" :rules="rules" label-width="80px">
         <el-form-item label="鍙傛暟鍚嶇О" prop="configName">
           <el-input v-model="form.configName" placeholder="璇疯緭鍏ュ弬鏁板悕绉�" />
@@ -105,7 +105,7 @@
         </el-form-item>
         <el-form-item label="绯荤粺鍐呯疆" prop="configType">
           <el-radio-group v-model="form.configType">
-            <el-radio v-for="dict in sys_yes_no" :key="dict.value" :label="dict.value">{{ dict.label }}</el-radio>
+            <el-radio v-for="dict in sys_yes_no" :key="dict.value" :value="dict.value">{{ dict.label }}</el-radio>
           </el-radio-group>
         </el-form-item>
         <el-form-item label="澶囨敞" prop="remark">
@@ -123,11 +123,11 @@
 </template>
 
 <script setup name="Config" lang="ts">
-import { listConfig, getConfig, delConfig, addConfig, updateConfig, refreshCache } from "@/api/system/config";
-import { ConfigForm, ConfigQuery, ConfigVO } from "@/api/system/config/types";
+import { listConfig, getConfig, delConfig, addConfig, updateConfig, refreshCache } from '@/api/system/config';
+import { ConfigForm, ConfigQuery, ConfigVO } from '@/api/system/config/types';
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
-const { sys_yes_no } = toRefs<any>(proxy?.useDict("sys_yes_no"));
+const { sys_yes_no } = toRefs<any>(proxy?.useDict('sys_yes_no'));
 
 const configList = ref<ConfigVO[]>([]);
 const loading = ref(true);
@@ -149,9 +149,9 @@
   configName: '',
   configKey: '',
   configValue: '',
-  configType: "Y",
+  configType: 'Y',
   remark: ''
-}
+};
 const data = reactive<PageData<ConfigForm, ConfigQuery>>({
   form: { ...initFormData },
   queryParams: {
@@ -159,12 +159,12 @@
     pageSize: 10,
     configName: '',
     configKey: '',
-    configType: '',
+    configType: ''
   },
   rules: {
-    configName: [{ required: true, message: "鍙傛暟鍚嶇О涓嶈兘涓虹┖", trigger: "blur" }],
-    configKey: [{ required: true, message: "鍙傛暟閿悕涓嶈兘涓虹┖", trigger: "blur" }],
-    configValue: [{ required: true, message: "鍙傛暟閿�间笉鑳戒负绌�", trigger: "blur" }]
+    configName: [{ required: true, message: '鍙傛暟鍚嶇О涓嶈兘涓虹┖', trigger: 'blur' }],
+    configKey: [{ required: true, message: '鍙傛暟閿悕涓嶈兘涓虹┖', trigger: 'blur' }],
+    configValue: [{ required: true, message: '鍙傛暟閿�间笉鑳戒负绌�', trigger: 'blur' }]
   }
 });
 
@@ -177,40 +177,40 @@
   configList.value = res.rows;
   total.value = res.total;
   loading.value = false;
-}
+};
 /** 鍙栨秷鎸夐挳 */
 const cancel = () => {
   reset();
   dialog.visible = false;
-}
+};
 /** 琛ㄥ崟閲嶇疆 */
 const reset = () => {
   form.value = { ...initFormData };
   configFormRef.value?.resetFields();
-}
+};
 /** 鎼滅储鎸夐挳鎿嶄綔 */
 const handleQuery = () => {
   queryParams.value.pageNum = 1;
   getList();
-}
+};
 /** 閲嶇疆鎸夐挳鎿嶄綔 */
 const resetQuery = () => {
   dateRange.value = ['', ''];
   queryFormRef.value?.resetFields();
   handleQuery();
-}
+};
 /** 澶氶�夋閫変腑鏁版嵁 */
 const handleSelectionChange = (selection: ConfigVO[]) => {
-  ids.value = selection.map(item => item.configId);
+  ids.value = selection.map((item) => item.configId);
   single.value = selection.length != 1;
   multiple.value = !selection.length;
-}
+};
 /** 鏂板鎸夐挳鎿嶄綔 */
 const handleAdd = () => {
   reset();
   dialog.visible = true;
-  dialog.title = "娣诲姞鍙傛暟";
-}
+  dialog.title = '娣诲姞鍙傛暟';
+};
 /** 淇敼鎸夐挳鎿嶄綔 */
 const handleUpdate = async (row?: ConfigVO) => {
   reset();
@@ -218,40 +218,44 @@
   const res = await getConfig(configId);
   Object.assign(form.value, res.data);
   dialog.visible = true;
-  dialog.title = "淇敼鍙傛暟";
-}
+  dialog.title = '淇敼鍙傛暟';
+};
 /** 鎻愪氦鎸夐挳 */
 const submitForm = () => {
   configFormRef.value?.validate(async (valid: boolean) => {
     if (valid) {
       form.value.configId ? await updateConfig(form.value) : await addConfig(form.value);
-      proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+      proxy?.$modal.msgSuccess('鎿嶄綔鎴愬姛');
       dialog.visible = false;
       await getList();
     }
   });
-}
+};
 /** 鍒犻櫎鎸夐挳鎿嶄綔 */
 const handleDelete = async (row?: ConfigVO) => {
   const configIds = row?.configId || ids.value;
   await proxy?.$modal.confirm('鏄惁纭鍒犻櫎鍙傛暟缂栧彿涓�"' + configIds + '"鐨勬暟鎹」锛�');
   await delConfig(configIds);
   await getList();
-  proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-}
+  proxy?.$modal.msgSuccess('鍒犻櫎鎴愬姛');
+};
 /** 瀵煎嚭鎸夐挳鎿嶄綔 */
 const handleExport = () => {
-  proxy?.download("system/config/export", {
-    ...queryParams.value
-  }, `config_${new Date().getTime()}.xlsx`);
-}
+  proxy?.download(
+    'system/config/export',
+    {
+      ...queryParams.value
+    },
+    `config_${new Date().getTime()}.xlsx`
+  );
+};
 /** 鍒锋柊缂撳瓨鎸夐挳鎿嶄綔 */
 const handleRefreshCache = async () => {
   await refreshCache();
-  proxy?.$modal.msgSuccess("鍒锋柊缂撳瓨鎴愬姛");
-}
+  proxy?.$modal.msgSuccess('鍒锋柊缂撳瓨鎴愬姛');
+};
 
 onMounted(() => {
   getList();
-})
+});
 </script>
diff --git a/src/views/system/dept/index.vue b/src/views/system/dept/index.vue
index 9797125..71aeff1 100644
--- a/src/views/system/dept/index.vue
+++ b/src/views/system/dept/index.vue
@@ -1,14 +1,17 @@
 <template>
   <div class="p-2">
     <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
-      <div class="mb-[10px]" v-show="showSearch">
+      <div v-show="showSearch" class="mb-[10px]">
         <el-card shadow="hover">
-          <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
+          <el-form ref="queryFormRef" :model="queryParams" :inline="true">
             <el-form-item label="閮ㄩ棬鍚嶇О" prop="deptName">
-              <el-input v-model="queryParams.deptName" placeholder="璇疯緭鍏ラ儴闂ㄥ悕绉�" clearable style="width: 240px" @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.deptName" placeholder="璇疯緭鍏ラ儴闂ㄥ悕绉�" clearable @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="绫诲埆缂栫爜" prop="deptCategory">
+              <el-input v-model="queryParams.deptCategory" placeholder="璇疯緭鍏ョ被鍒紪鐮�" clearable style="width: 240px" @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item label="鐘舵��" prop="status">
-              <el-select v-model="queryParams.status" placeholder="閮ㄩ棬鐘舵��" clearable>
+              <el-select v-model="queryParams.status" placeholder="閮ㄩ棬鐘舵��" clearable >
                 <el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
               </el-select>
             </el-form-item>
@@ -25,24 +28,25 @@
       <template #header>
         <el-row :gutter="10">
           <el-col :span="1.5">
-            <el-button type="primary" plain icon="Plus" @click="handleAdd()" v-hasPermi="['system:dept:add']">鏂板 </el-button>
+            <el-button v-hasPermi="['system:dept:add']" type="primary" plain icon="Plus" @click="handleAdd()">鏂板 </el-button>
           </el-col>
           <el-col :span="1.5">
             <el-button type="info" plain icon="Sort" @click="handleToggleExpandAll">灞曞紑/鎶樺彔</el-button>
           </el-col>
-          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+          <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
         </el-row>
       </template>
 
       <el-table
+        ref="deptTableRef"
         v-loading="loading"
         :data="deptList"
         row-key="deptId"
         :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
-        ref="deptTableRef"
         :default-expand-all="isExpandAll"
       >
         <el-table-column prop="deptName" label="閮ㄩ棬鍚嶇О" width="260"></el-table-column>
+        <el-table-column prop="deptCategory" align="center" label="绫诲埆缂栫爜" width="200"></el-table-column>
         <el-table-column prop="orderNum" align="center" label="鎺掑簭" width="200"></el-table-column>
         <el-table-column prop="status" align="center" label="鐘舵��" width="100">
           <template #default="scope">
@@ -57,23 +61,23 @@
         <el-table-column fixed="right" align="center" label="鎿嶄綔">
           <template #default="scope">
             <el-tooltip content="淇敼" placement="top">
-              <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:dept:edit']" />
+              <el-button v-hasPermi="['system:dept:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)" />
             </el-tooltip>
             <el-tooltip content="鏂板" placement="top">
-              <el-button link type="primary" icon="Plus" @click="handleAdd(scope.row)" v-hasPermi="['system:dept:add']" />
+              <el-button v-hasPermi="['system:dept:add']" link type="primary" icon="Plus" @click="handleAdd(scope.row)" />
             </el-tooltip>
             <el-tooltip content="鍒犻櫎" placement="top">
-              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:dept:remove']" />
+              <el-button v-hasPermi="['system:dept:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)" />
             </el-tooltip>
           </template>
         </el-table-column>
       </el-table>
     </el-card>
 
-    <el-dialog :title="dialog.title" v-model="dialog.visible" destroy-on-close append-to-body width="600px">
+    <el-dialog v-model="dialog.visible" :title="dialog.title" destroy-on-close append-to-body width="600px">
       <el-form ref="deptFormRef" :model="form" :rules="rules" label-width="80px">
         <el-row>
-          <el-col :span="24" v-if="form.parentId !== 0">
+          <el-col v-if="form.parentId !== 0" :span="24">
             <el-form-item label="涓婄骇閮ㄩ棬" prop="parentId">
               <el-tree-select
                 v-model="form.parentId"
@@ -88,6 +92,11 @@
           <el-col :span="12">
             <el-form-item label="閮ㄩ棬鍚嶇О" prop="deptName">
               <el-input v-model="form.deptName" placeholder="璇疯緭鍏ラ儴闂ㄥ悕绉�" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="绫诲埆缂栫爜" prop="deptCategory">
+              <el-input v-model="form.deptCategory" placeholder="璇疯緭鍏ョ被鍒紪鐮�" />
             </el-form-item>
           </el-col>
           <el-col :span="12">
@@ -115,8 +124,7 @@
           <el-col :span="12">
             <el-form-item label="閮ㄩ棬鐘舵��">
               <el-radio-group v-model="form.status">
-                <el-radio v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.value">{{ dict.label
-                }}</el-radio>
+                <el-radio v-for="dict in sys_normal_disable" :key="dict.value" :value="dict.value">{{ dict.label }}</el-radio>
               </el-radio-group>
             </el-form-item>
           </el-col>
@@ -133,26 +141,25 @@
 </template>
 
 <script setup name="Dept" lang="ts">
-import { listDept, getDept, delDept, addDept, updateDept, listDeptExcludeChild } from "@/api/system/dept"
-import { DeptForm, DeptQuery, DeptVO } from "@/api/system/dept/types";
-import {UserVO} from "@/api/system/user/types";
-import {listUserByDeptId} from "@/api/system/user";
+import { listDept, getDept, delDept, addDept, updateDept, listDeptExcludeChild } from '@/api/system/dept';
+import { DeptForm, DeptQuery, DeptVO } from '@/api/system/dept/types';
+import { UserVO } from '@/api/system/user/types';
+import { listUserByDeptId } from '@/api/system/user';
 
 interface DeptOptionsType {
   deptId: number | string;
   deptName: string;
   children: DeptOptionsType[];
-
 }
 
-const { proxy } = getCurrentInstance() as ComponentInternalInstance
-const { sys_normal_disable } = toRefs<any>(proxy?.useDict("sys_normal_disable"));
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_normal_disable'));
 
-const deptList = ref<DeptVO[]>([])
-const loading = ref(true)
-const showSearch = ref(true)
-const deptOptions = ref<DeptOptionsType[]>([])
-const isExpandAll = ref(true)
+const deptList = ref<DeptVO[]>([]);
+const loading = ref(true);
+const showSearch = ref(true);
+const deptOptions = ref<DeptOptionsType[]>([]);
+const isExpandAll = ref(true);
 const deptUserList = ref<UserVO[]>([]);
 
 const dialog = reactive<DialogOption>({
@@ -168,45 +175,48 @@
   deptId: undefined,
   parentId: undefined,
   deptName: undefined,
+  deptCategory: undefined,
   orderNum: 0,
   leader: undefined,
   phone: undefined,
   email: undefined,
-  status: "0"
-}
-const data = reactive<PageData<DeptForm, DeptQuery>>({
+  status: '0'
+};
+const initData: PageData<DeptForm, DeptQuery> = {
   form: { ...initFormData },
   queryParams: {
     pageNum: 1,
     pageSize: 10,
     deptName: undefined,
+    deptCategory: undefined,
     status: undefined
   },
   rules: {
-    parentId: [{ required: true, message: "涓婄骇閮ㄩ棬涓嶈兘涓虹┖", trigger: "blur" }],
-    deptName: [{ required: true, message: "閮ㄩ棬鍚嶇О涓嶈兘涓虹┖", trigger: "blur" }],
-    orderNum: [{ required: true, message: "鏄剧ず鎺掑簭涓嶈兘涓虹┖", trigger: "blur" }],
-    email: [{ type: "email", message: "璇疯緭鍏ユ纭殑閭鍦板潃", trigger: ["blur", "change"] }],
-    phone: [{ pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "璇疯緭鍏ユ纭殑鎵嬫満鍙风爜", trigger: "blur" }]
-  },
-})
+    parentId: [{ required: true, message: '涓婄骇閮ㄩ棬涓嶈兘涓虹┖', trigger: 'blur' }],
+    deptName: [{ required: true, message: '閮ㄩ棬鍚嶇О涓嶈兘涓虹┖', trigger: 'blur' }],
+    orderNum: [{ required: true, message: '鏄剧ず鎺掑簭涓嶈兘涓虹┖', trigger: 'blur' }],
+    email: [{ type: 'email', message: '璇疯緭鍏ユ纭殑閭鍦板潃', trigger: ['blur', 'change'] }],
+    phone: [{ pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: '璇疯緭鍏ユ纭殑鎵嬫満鍙风爜', trigger: 'blur' }]
+  }
+};
+const data = reactive<PageData<DeptForm, DeptQuery>>(initData);
 
-const { queryParams, form, rules } = toRefs<PageData<DeptForm, DeptQuery>>(data)
+const { queryParams, form, rules } = toRefs<PageData<DeptForm, DeptQuery>>(data);
 
 /** 鏌ヨ鑿滃崟鍒楄〃 */
 const getList = async () => {
   loading.value = true;
   const res = await listDept(queryParams.value);
-  const data = proxy?.handleTree<DeptVO>(res.data, "deptId")
+  const data = proxy?.handleTree<DeptVO>(res.data, 'deptId');
   if (data) {
-    deptList.value = data
+    deptList.value = data;
   }
-  loading.value = false
-}
+  loading.value = false;
+};
 
 /** 鏌ヨ褰撳墠閮ㄩ棬鐨勬墍鏈夌敤鎴� */
 async function getDeptAllUser(deptId: any) {
-  if (deptId !== null && deptId !== "" && deptId !== undefined) {
+  if (deptId !== null && deptId !== '' && deptId !== undefined) {
     const res = await listUserByDeptId(deptId);
     deptUserList.value = res.data;
   }
@@ -214,52 +224,52 @@
 
 /** 鍙栨秷鎸夐挳 */
 const cancel = () => {
-  reset()
-  dialog.visible = false
-}
+  reset();
+  dialog.visible = false;
+};
 /** 琛ㄥ崟閲嶇疆 */
 const reset = () => {
   form.value = { ...initFormData };
   deptFormRef.value?.resetFields();
-}
+};
 
 /** 鎼滅储鎸夐挳鎿嶄綔 */
 const handleQuery = () => {
   getList();
-}
+};
 /** 閲嶇疆鎸夐挳鎿嶄綔 */
 const resetQuery = () => {
   queryFormRef.value?.resetFields();
-  handleQuery()
-}
+  handleQuery();
+};
 
 /** 灞曞紑/鎶樺彔鎿嶄綔 */
 const handleToggleExpandAll = () => {
   isExpandAll.value = !isExpandAll.value;
-  toggleExpandAll(deptList.value, isExpandAll.value)
-}
+  toggleExpandAll(deptList.value, isExpandAll.value);
+};
 /** 灞曞紑/鎶樺彔鎵�鏈� */
 const toggleExpandAll = (data: DeptVO[], status: boolean) => {
   data.forEach((item) => {
-    deptTableRef.value?.toggleRowExpansion(item, status)
-    if (item.children && item.children.length > 0) toggleExpandAll(item.children, status)
-  })
-}
+    deptTableRef.value?.toggleRowExpansion(item, status);
+    if (item.children && item.children.length > 0) toggleExpandAll(item.children, status);
+  });
+};
 
 /** 鏂板鎸夐挳鎿嶄綔 */
 const handleAdd = async (row?: DeptVO) => {
   reset();
   const res = await listDept();
-  const data = proxy?.handleTree<DeptOptionsType>(res.data, "deptId");
+  const data = proxy?.handleTree<DeptOptionsType>(res.data, 'deptId');
   if (data) {
-    deptOptions.value = data
+    deptOptions.value = data;
     if (row && row.deptId) {
       form.value.parentId = row?.deptId;
     }
     dialog.visible = true;
-    dialog.title = "娣诲姞閮ㄩ棬";
+    dialog.title = '娣诲姞閮ㄩ棬';
   }
-}
+};
 
 /** 淇敼鎸夐挳鎿嶄綔 */
 const handleUpdate = async (row: DeptVO) => {
@@ -267,9 +277,9 @@
   //鏌ヨ褰撳墠閮ㄩ棬鎵�鏈夌敤鎴�
   getDeptAllUser(row.deptId);
   const res = await getDept(row.deptId);
-  form.value = res.data
+  form.value = res.data;
   const response = await listDeptExcludeChild(row.deptId);
-  const data = proxy?.handleTree<DeptOptionsType>(response.data, "deptId")
+  const data = proxy?.handleTree<DeptOptionsType>(response.data, 'deptId');
   if (data) {
     deptOptions.value = data;
     if (data.length === 0) {
@@ -282,26 +292,26 @@
     }
   }
   dialog.visible = true;
-  dialog.title = "淇敼閮ㄩ棬";
-}
+  dialog.title = '淇敼閮ㄩ棬';
+};
 /** 鎻愪氦鎸夐挳 */
 const submitForm = () => {
   deptFormRef.value?.validate(async (valid: boolean) => {
     if (valid) {
       form.value.deptId ? await updateDept(form.value) : await addDept(form.value);
-      proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+      proxy?.$modal.msgSuccess('鎿嶄綔鎴愬姛');
       dialog.visible = false;
       await getList();
     }
-  })
-}
+  });
+};
 /** 鍒犻櫎鎸夐挳鎿嶄綔 */
 const handleDelete = async (row: DeptVO) => {
   await proxy?.$modal.confirm('鏄惁纭鍒犻櫎鍚嶇О涓�"' + row.deptName + '"鐨勬暟鎹」?');
   await delDept(row.deptId);
   await getList();
-  proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-}
+  proxy?.$modal.msgSuccess('鍒犻櫎鎴愬姛');
+};
 
 onMounted(() => {
   getList();
diff --git a/src/views/system/dict/data.vue b/src/views/system/dict/data.vue
index c2ef550..33c7fc8 100644
--- a/src/views/system/dict/data.vue
+++ b/src/views/system/dict/data.vue
@@ -1,16 +1,16 @@
 <template>
   <div class="p-2">
     <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
-      <div class="mb-[10px]" v-show="showSearch">
+      <div v-show="showSearch" class="mb-[10px]">
         <el-card shadow="hover">
-          <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
+          <el-form ref="queryFormRef" :model="queryParams" :inline="true">
             <el-form-item label="瀛楀吀鍚嶇О" prop="dictType">
-              <el-select v-model="queryParams.dictType" style="width: 200px">
+              <el-select v-model="queryParams.dictType">
                 <el-option v-for="item in typeOptions" :key="item.dictId" :label="item.dictName" :value="item.dictType" />
               </el-select>
             </el-form-item>
             <el-form-item label="瀛楀吀鏍囩" prop="dictLabel">
-              <el-input v-model="queryParams.dictLabel" placeholder="璇疯緭鍏ュ瓧鍏告爣绛�" clearable style="width: 200px" @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.dictLabel" placeholder="璇疯緭鍏ュ瓧鍏告爣绛�" clearable @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item>
               <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
@@ -24,33 +24,41 @@
       <template #header>
         <el-row :gutter="10" class="mb8">
           <el-col :span="1.5">
-            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:dict:add']">鏂板</el-button>
+            <el-button v-hasPermi="['system:dict:add']" type="primary" plain icon="Plus" @click="handleAdd">鏂板</el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:dict:edit']">淇敼</el-button>
+            <el-button v-hasPermi="['system:dict:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()">淇敼</el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:dict:remove']">
+            <el-button v-hasPermi="['system:dict:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">
               鍒犻櫎
             </el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:dict:export']">瀵煎嚭</el-button>
+            <el-button v-hasPermi="['system:dict:export']" type="warning" plain icon="Download" @click="handleExport">瀵煎嚭</el-button>
           </el-col>
           <el-col :span="1.5">
             <el-button type="warning" plain icon="Close" @click="handleClose">鍏抽棴</el-button>
           </el-col>
-          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+          <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
         </el-row>
       </template>
 
       <el-table v-loading="loading" :data="dataList" @selection-change="handleSelectionChange">
         <el-table-column type="selection" width="55" align="center" />
-        <el-table-column label="瀛楀吀缂栫爜" align="center" prop="dictCode" v-if="false" />
+        <el-table-column v-if="false" label="瀛楀吀缂栫爜" align="center" prop="dictCode" />
         <el-table-column label="瀛楀吀鏍囩" align="center" prop="dictLabel">
           <template #default="scope">
-            <span v-if="(scope.row.listClass === '' || scope.row.listClass === 'default') && (scope.row.cssClass === '' || scope.row.cssClass == null)">{{ scope.row.dictLabel }}</span>
-            <el-tag v-else :type="(scope.row.listClass === 'primary' || scope.row.listClass === 'default') ? '' : scope.row.listClass" :class="scope.row.cssClass">{{ scope.row.dictLabel }}</el-tag>
+            <span
+              v-if="(scope.row.listClass === '' || scope.row.listClass === 'default') && (scope.row.cssClass === '' || scope.row.cssClass == null)"
+              >{{ scope.row.dictLabel }}</span
+            >
+            <el-tag
+              v-else
+              :type="scope.row.listClass === 'primary' || scope.row.listClass === 'default' ? 'primary' : scope.row.listClass"
+              :class="scope.row.cssClass"
+              >{{ scope.row.dictLabel }}</el-tag
+            >
           </template>
         </el-table-column>
         <el-table-column label="瀛楀吀閿��" align="center" prop="dictValue" />
@@ -64,19 +72,19 @@
         <el-table-column label="鎿嶄綔" align="center" width="160" 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="['system:dict:edit']"></el-button>
+              <el-button v-hasPermi="['system:dict:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
             </el-tooltip>
             <el-tooltip content="鍒犻櫎" placement="top">
-              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:dict:remove']"></el-button>
+              <el-button v-hasPermi="['system:dict:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></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" />
+      <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
     </el-card>
     <!-- 娣诲姞鎴栦慨鏀瑰弬鏁伴厤缃璇濇 -->
-    <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+    <el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
       <el-form ref="dataFormRef" :model="form" :rules="rules" label-width="80px">
         <el-form-item label="瀛楀吀绫诲瀷">
           <el-input v-model="form.dictType" :disabled="true" />
@@ -118,13 +126,14 @@
 </template>
 
 <script setup name="Data" lang="ts">
-import useDictStore from '@/store/modules/dict'
-import { optionselect as getDictOptionselect, getType } from "@/api/system/dict/type";
-import { listData, getData, delData, addData, updateData } from "@/api/system/dict/data";
+import useDictStore from '@/store/modules/dict';
+import { optionselect as getDictOptionselect, getType } from '@/api/system/dict/type';
+import { listData, getData, delData, addData, updateData } from '@/api/system/dict/data';
 import { DictTypeVO } from '@/api/system/dict/type/types';
-import { DictDataForm, DictDataQuery, DictDataVO } from "@/api/system/dict/data/types";
+import { DictDataForm, DictDataQuery, DictDataVO } from '@/api/system/dict/data/types';
+import { RouteLocationNormalized } from 'vue-router';
 
-const { proxy } = getCurrentInstance() as ComponentInternalInstance
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 const route = useRoute();
 
 const dataList = ref<DictDataVO[]>([]);
@@ -134,12 +143,11 @@
 const single = ref(true);
 const multiple = ref(true);
 const total = ref(0);
-const defaultDictType = ref("");
+const defaultDictType = ref('');
 const typeOptions = ref<DictTypeVO[]>([]);
 
 const dataFormRef = ref<ElFormInstance>();
 const queryFormRef = ref<ElFormInstance>();
-
 
 const dialog = reactive<DialogOption>({
   visible: false,
@@ -147,13 +155,13 @@
 });
 
 // 鏁版嵁鏍囩鍥炴樉鏍峰紡
-const listClassOptions = ref<Array<{ value: string, label: string }>>([
-  { value: "default", label: "榛樿" },
-  { value: "primary", label: "涓昏" },
-  { value: "success", label: "鎴愬姛" },
-  { value: "info", label: "淇℃伅" },
-  { value: "warning", label: "璀﹀憡" },
-  { value: "danger", label: "鍗遍櫓" }
+const listClassOptions = ref<Array<{ value: string; label: string }>>([
+  { value: 'default', label: '榛樿' },
+  { value: 'primary', label: '涓昏' },
+  { value: 'success', label: '鎴愬姛' },
+  { value: 'info', label: '淇℃伅' },
+  { value: 'warning', label: '璀﹀憡' },
+  { value: 'danger', label: '鍗遍櫓' }
 ]);
 
 const initFormData: DictDataForm = {
@@ -161,10 +169,10 @@
   dictLabel: '',
   dictValue: '',
   cssClass: '',
-  listClass: "default",
+  listClass: 'primary',
   dictSort: 0,
   remark: ''
-}
+};
 const data = reactive<PageData<DictDataForm, DictDataQuery>>({
   form: { ...initFormData },
   queryParams: {
@@ -175,9 +183,9 @@
     dictLabel: ''
   },
   rules: {
-    dictLabel: [{ required: true, message: "鏁版嵁鏍囩涓嶈兘涓虹┖", trigger: "blur" }],
-    dictValue: [{ required: true, message: "鏁版嵁閿�间笉鑳戒负绌�", trigger: "blur" }],
-    dictSort: [{ required: true, message: "鏁版嵁椤哄簭涓嶈兘涓虹┖", trigger: "blur" }]
+    dictLabel: [{ required: true, message: '鏁版嵁鏍囩涓嶈兘涓虹┖', trigger: 'blur' }],
+    dictValue: [{ required: true, message: '鏁版嵁閿�间笉鑳戒负绌�', trigger: 'blur' }],
+    dictSort: [{ required: true, message: '鏁版嵁椤哄簭涓嶈兘涓虹┖', trigger: 'blur' }]
   }
 });
 
@@ -189,13 +197,13 @@
   queryParams.value.dictType = data.dictType;
   defaultDictType.value = data.dictType;
   getList();
-}
+};
 
 /** 鏌ヨ瀛楀吀绫诲瀷鍒楄〃 */
 const getTypeList = async () => {
-  const res = await getDictOptionselect()
+  const res = await getDictOptionselect();
   typeOptions.value = res.data;
-}
+};
 /** 鏌ヨ瀛楀吀鏁版嵁鍒楄〃 */
 const getList = async () => {
   loading.value = true;
@@ -203,46 +211,56 @@
   dataList.value = res.rows;
   total.value = res.total;
   loading.value = false;
-}
+};
 /** 鍙栨秷鎸夐挳 */
 const cancel = () => {
   dialog.visible = false;
   reset();
-}
+};
 /** 琛ㄥ崟閲嶇疆 */
 const reset = () => {
   form.value = { ...initFormData };
   dataFormRef.value?.resetFields();
-}
+};
 /** 鎼滅储鎸夐挳鎿嶄綔 */
 const handleQuery = () => {
   queryParams.value.pageNum = 1;
   getList();
-}
+};
 /** 杩斿洖鎸夐挳鎿嶄綔 */
 const handleClose = () => {
-  const obj = { path: "/system/dict" };
+  const obj: RouteLocationNormalized = {
+    fullPath: '',
+    hash: '',
+    matched: [],
+    meta: undefined,
+    name: undefined,
+    params: undefined,
+    query: undefined,
+    redirectedFrom: undefined,
+    path: '/system/dict'
+  };
   proxy?.$tab.closeOpenPage(obj);
-}
+};
 /** 閲嶇疆鎸夐挳鎿嶄綔 */
 const resetQuery = () => {
   queryFormRef.value?.resetFields();
   queryParams.value.dictType = defaultDictType.value;
   handleQuery();
-}
+};
 /** 鏂板鎸夐挳鎿嶄綔 */
 const handleAdd = () => {
   reset();
   form.value.dictType = queryParams.value.dictType;
   dialog.visible = true;
-  dialog.title = "娣诲姞瀛楀吀鏁版嵁";
-}
+  dialog.title = '娣诲姞瀛楀吀鏁版嵁';
+};
 /** 澶氶�夋閫変腑鏁版嵁 */
 const handleSelectionChange = (selection: DictDataVO[]) => {
-  ids.value = selection.map(item => item.dictCode);
+  ids.value = selection.map((item) => item.dictCode);
   single.value = selection.length != 1;
   multiple.value = !selection.length;
-}
+};
 /** 淇敼鎸夐挳鎿嶄綔 */
 const handleUpdate = async (row?: DictDataVO) => {
   reset();
@@ -250,40 +268,42 @@
   const res = await getData(dictCode);
   Object.assign(form.value, res.data);
   dialog.visible = true;
-  dialog.title = "淇敼瀛楀吀鏁版嵁";
-}
+  dialog.title = '淇敼瀛楀吀鏁版嵁';
+};
 /** 鎻愪氦鎸夐挳 */
 const submitForm = () => {
   dataFormRef.value?.validate(async (valid: boolean) => {
     if (valid) {
       form.value.dictCode ? await updateData(form.value) : await addData(form.value);
       useDictStore().removeDict(queryParams.value.dictType);
-      proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+      proxy?.$modal.msgSuccess('鎿嶄綔鎴愬姛');
       dialog.visible = false;
       await getList();
-
     }
   });
-}
+};
 /** 鍒犻櫎鎸夐挳鎿嶄綔 */
 const handleDelete = async (row?: DictDataVO) => {
   const dictCodes = row?.dictCode || ids.value;
   await proxy?.$modal.confirm('鏄惁纭鍒犻櫎瀛楀吀缂栫爜涓�"' + dictCodes + '"鐨勬暟鎹」锛�');
   await delData(dictCodes);
   await getList();
-  proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+  proxy?.$modal.msgSuccess('鍒犻櫎鎴愬姛');
   useDictStore().removeDict(queryParams.value.dictType);
-
-}
+};
 /** 瀵煎嚭鎸夐挳鎿嶄綔 */
 const handleExport = () => {
-  proxy?.download("system/dict/data/export", {
-    ...queryParams.value
-  }, `dict_data_${new Date().getTime()}.xlsx`);
-}
+  proxy?.download(
+    'system/dict/data/export',
+    {
+      ...queryParams.value
+    },
+    `dict_data_${new Date().getTime()}.xlsx`
+  );
+};
 
 onMounted(() => {
-  getTypes(route.params && route.params.dictId as string);
+  getTypes(route.params && (route.params.dictId as string));
   getTypeList();
-})
+});
 </script>
diff --git a/src/views/system/dict/index.vue b/src/views/system/dict/index.vue
index f3b0051..d44a04a 100644
--- a/src/views/system/dict/index.vue
+++ b/src/views/system/dict/index.vue
@@ -1,14 +1,14 @@
 <template>
   <div class="p-2">
     <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
-      <div class="mb-[10px]" v-show="showSearch">
+      <div v-show="showSearch" class="mb-[10px]">
         <el-card shadow="hover">
-          <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
+          <el-form ref="queryFormRef" :model="queryParams" :inline="true">
             <el-form-item label="瀛楀吀鍚嶇О" prop="dictName">
-              <el-input v-model="queryParams.dictName" placeholder="璇疯緭鍏ュ瓧鍏稿悕绉�" clearable style="width: 240px" @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.dictName" placeholder="璇疯緭鍏ュ瓧鍏稿悕绉�" clearable @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item label="瀛楀吀绫诲瀷" prop="dictType">
-              <el-input v-model="queryParams.dictType" placeholder="璇疯緭鍏ュ瓧鍏哥被鍨�" clearable style="width: 240px" @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.dictType" placeholder="璇疯緭鍏ュ瓧鍏哥被鍨�" clearable @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item label="鍒涘缓鏃堕棿" style="width: 308px">
               <el-date-picker
@@ -33,29 +33,29 @@
       <template #header>
         <el-row :gutter="10" class="mb8">
           <el-col :span="1.5">
-            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:dict:add']">鏂板</el-button>
+            <el-button v-hasPermi="['system:dict:add']" type="primary" plain icon="Plus" @click="handleAdd">鏂板</el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:dict:edit']">淇敼</el-button>
+            <el-button v-hasPermi="['system:dict:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()">淇敼</el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:dict:remove']">
+            <el-button v-hasPermi="['system:dict:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">
               鍒犻櫎
             </el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:dict:export']">瀵煎嚭</el-button>
+            <el-button v-hasPermi="['system:dict:export']" type="warning" plain icon="Download" @click="handleExport">瀵煎嚭</el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="danger" plain icon="Refresh" @click="handleRefreshCache" v-hasPermi="['system:dict:remove']">鍒锋柊缂撳瓨</el-button>
+            <el-button v-hasPermi="['system:dict:remove']" type="danger" plain icon="Refresh" @click="handleRefreshCache">鍒锋柊缂撳瓨</el-button>
           </el-col>
-          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+          <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
         </el-row>
       </template>
 
       <el-table v-loading="loading" :data="typeList" @selection-change="handleSelectionChange">
         <el-table-column type="selection" width="55" align="center" />
-        <el-table-column label="瀛楀吀缂栧彿" align="center" prop="dictId" v-if="false" />
+        <el-table-column v-if="false" label="瀛楀吀缂栧彿" align="center" prop="dictId" />
         <el-table-column label="瀛楀吀鍚嶇О" align="center" prop="dictName" :show-overflow-tooltip="true" />
         <el-table-column label="瀛楀吀绫诲瀷" align="center" :show-overflow-tooltip="true">
           <template #default="scope">
@@ -73,19 +73,19 @@
         <el-table-column label="鎿嶄綔" align="center" width="160" 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="['system:dict:edit']"></el-button>
+              <el-button v-hasPermi="['system:dict:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
             </el-tooltip>
             <el-tooltip content="鍒犻櫎" placement="top">
-              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:dict:remove']"></el-button>
+              <el-button v-hasPermi="['system:dict:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></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" />
+      <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
     </el-card>
     <!-- 娣诲姞鎴栦慨鏀瑰弬鏁伴厤缃璇濇 -->
-    <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+    <el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
       <el-form ref="dictFormRef" :model="form" :rules="rules" label-width="80px">
         <el-form-item label="瀛楀吀鍚嶇О" prop="dictName">
           <el-input v-model="form.dictName" placeholder="璇疯緭鍏ュ瓧鍏稿悕绉�" />
@@ -108,9 +108,9 @@
 </template>
 
 <script setup name="Dict" lang="ts">
-import useDictStore from '@/store/modules/dict'
-import { listType, getType, delType, addType, updateType, refreshCache } from "@/api/system/dict/type";
-import { DictTypeForm, DictTypeQuery, DictTypeVO } from "@/api/system/dict/type/types";
+import useDictStore from '@/store/modules/dict';
+import { listType, getType, delType, addType, updateType, refreshCache } from '@/api/system/dict/type';
+import { DictTypeForm, DictTypeQuery, DictTypeVO } from '@/api/system/dict/type/types';
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
@@ -126,7 +126,6 @@
 const dictFormRef = ref<ElFormInstance>();
 const queryFormRef = ref<ElFormInstance>();
 
-
 const dialog = reactive<DialogOption>({
   visible: false,
   title: ''
@@ -137,7 +136,7 @@
   dictName: '',
   dictType: '',
   remark: ''
-}
+};
 const data = reactive<PageData<DictTypeForm, DictTypeQuery>>({
   form: { ...initFormData },
   queryParams: {
@@ -147,9 +146,9 @@
     dictType: ''
   },
   rules: {
-    dictName: [{ required: true, message: "瀛楀吀鍚嶇О涓嶈兘涓虹┖", trigger: "blur" }],
-    dictType: [{ required: true, message: "瀛楀吀绫诲瀷涓嶈兘涓虹┖", trigger: "blur" }]
-  },
+    dictName: [{ required: true, message: '瀛楀吀鍚嶇О涓嶈兘涓虹┖', trigger: 'blur' }],
+    dictType: [{ required: true, message: '瀛楀吀绫诲瀷涓嶈兘涓虹┖', trigger: 'blur' }]
+  }
 });
 
 const { queryParams, form, rules } = toRefs(data);
@@ -157,45 +156,45 @@
 /** 鏌ヨ瀛楀吀绫诲瀷鍒楄〃 */
 const getList = () => {
   loading.value = true;
-  listType(proxy?.addDateRange(queryParams.value, dateRange.value)).then(res => {
+  listType(proxy?.addDateRange(queryParams.value, dateRange.value)).then((res) => {
     typeList.value = res.rows;
     total.value = res.total;
     loading.value = false;
   });
-}
+};
 /** 鍙栨秷鎸夐挳 */
 const cancel = () => {
   reset();
   dialog.visible = false;
-}
+};
 /** 琛ㄥ崟閲嶇疆 */
 const reset = () => {
   form.value = { ...initFormData };
   dictFormRef.value?.resetFields();
-}
+};
 /** 鎼滅储鎸夐挳鎿嶄綔 */
 const handleQuery = () => {
   queryParams.value.pageNum = 1;
   getList();
-}
+};
 /** 閲嶇疆鎸夐挳鎿嶄綔 */
 const resetQuery = () => {
   dateRange.value = ['', ''];
   queryFormRef.value?.resetFields();
   handleQuery();
-}
+};
 /** 鏂板鎸夐挳鎿嶄綔 */
 const handleAdd = () => {
   reset();
   dialog.visible = true;
-  dialog.title = "娣诲姞瀛楀吀绫诲瀷";
-}
+  dialog.title = '娣诲姞瀛楀吀绫诲瀷';
+};
 /** 澶氶�夋閫変腑鏁版嵁 */
 const handleSelectionChange = (selection: DictTypeVO[]) => {
-  ids.value = selection.map(item => item.dictId);
+  ids.value = selection.map((item) => item.dictId);
   single.value = selection.length != 1;
   multiple.value = !selection.length;
-}
+};
 /** 淇敼鎸夐挳鎿嶄綔 */
 const handleUpdate = async (row?: DictTypeVO) => {
   reset();
@@ -203,41 +202,45 @@
   const res = await getType(dictId);
   Object.assign(form.value, res.data);
   dialog.visible = true;
-  dialog.title = "淇敼瀛楀吀绫诲瀷";
-}
+  dialog.title = '淇敼瀛楀吀绫诲瀷';
+};
 /** 鎻愪氦鎸夐挳 */
 const submitForm = () => {
   dictFormRef.value?.validate(async (valid: boolean) => {
     if (valid) {
       form.value.dictId ? await updateType(form.value) : await addType(form.value);
-      proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+      proxy?.$modal.msgSuccess('鎿嶄綔鎴愬姛');
       dialog.visible = false;
       getList();
     }
   });
-}
+};
 /** 鍒犻櫎鎸夐挳鎿嶄綔 */
 const handleDelete = async (row?: DictTypeVO) => {
   const dictIds = row?.dictId || ids.value;
   await proxy?.$modal.confirm('鏄惁纭鍒犻櫎瀛楀吀缂栧彿涓�"' + dictIds + '"鐨勬暟鎹」锛�');
   await delType(dictIds);
   getList();
-  proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-}
+  proxy?.$modal.msgSuccess('鍒犻櫎鎴愬姛');
+};
 /** 瀵煎嚭鎸夐挳鎿嶄綔 */
 const handleExport = () => {
-  proxy?.download("system/dict/type/export", {
-    ...queryParams.value
-  }, `dict_${new Date().getTime()}.xlsx`);
-}
+  proxy?.download(
+    'system/dict/type/export',
+    {
+      ...queryParams.value
+    },
+    `dict_${new Date().getTime()}.xlsx`
+  );
+};
 /** 鍒锋柊缂撳瓨鎸夐挳鎿嶄綔 */
 const handleRefreshCache = async () => {
   await refreshCache();
-  proxy?.$modal.msgSuccess("鍒锋柊鎴愬姛");
+  proxy?.$modal.msgSuccess('鍒锋柊鎴愬姛');
   useDictStore().cleanDict();
-}
+};
 
 onMounted(() => {
   getList();
-})
+});
 </script>
diff --git a/src/views/system/menu/index.vue b/src/views/system/menu/index.vue
index 06cd928..9e5b5c3 100644
--- a/src/views/system/menu/index.vue
+++ b/src/views/system/menu/index.vue
@@ -1,14 +1,14 @@
 <template>
   <div class="p-2">
     <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
-      <div class="mb-[10px]" v-show="showSearch">
+      <div v-show="showSearch" class="mb-[10px]">
         <el-card shadow="hover">
-          <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
+          <el-form ref="queryFormRef" :model="queryParams" :inline="true">
             <el-form-item label="鑿滃崟鍚嶇О" prop="menuName">
-              <el-input v-model="queryParams.menuName" placeholder="璇疯緭鍏ヨ彍鍗曞悕绉�" clearable style="width: 240px" @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.menuName" placeholder="璇疯緭鍏ヨ彍鍗曞悕绉�" clearable @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item label="鐘舵��" prop="status">
-              <el-select v-model="queryParams.status" placeholder="鑿滃崟鐘舵��" clearable>
+              <el-select v-model="queryParams.status" placeholder="鑿滃崟鐘舵��" clearable >
                 <el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
               </el-select>
             </el-form-item>
@@ -25,21 +25,21 @@
       <template #header>
         <el-row :gutter="10">
           <el-col :span="1.5">
-            <el-button type="primary" plain icon="Plus" @click="handleAdd()" v-hasPermi="['system:menu:add']">鏂板 </el-button>
+            <el-button v-hasPermi="['system:menu:add']" type="primary" plain icon="Plus" @click="handleAdd()">鏂板 </el-button>
           </el-col>
           <el-col :span="1.5">
             <el-button type="info" plain icon="Sort" @click="handleToggleExpandAll">灞曞紑/鎶樺彔</el-button>
           </el-col>
-          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+          <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
         </el-row>
       </template>
 
       <el-table
+        ref="menuTableRef"
         v-loading="loading"
         :data="menuList"
         row-key="menuId"
         :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
-        ref="menuTableRef"
         :default-expand-all="isExpandAll"
       >
         <el-table-column prop="menuName" label="鑿滃崟鍚嶇О" :show-overflow-tooltip="true" width="160"></el-table-column>
@@ -64,20 +64,20 @@
         <el-table-column fixed="right" label="鎿嶄綔" width="180">
           <template #default="scope">
             <el-tooltip content="淇敼" placement="top">
-              <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:menu:edit']" />
+              <el-button v-hasPermi="['system:menu:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)" />
             </el-tooltip>
             <el-tooltip content="鏂板" placement="top">
-              <el-button link type="primary" icon="Plus" @click="handleAdd(scope.row)" v-hasPermi="['system:menu:add']" />
+              <el-button v-hasPermi="['system:menu:add']" link type="primary" icon="Plus" @click="handleAdd(scope.row)" />
             </el-tooltip>
             <el-tooltip content="鍒犻櫎" placement="top">
-              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:menu:remove']" />
+              <el-button v-hasPermi="['system:menu:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)" />
             </el-tooltip>
           </template>
         </el-table-column>
       </el-table>
     </el-card>
 
-    <el-dialog :title="dialog.title" v-model="dialog.visible" destroy-on-close append-to-bod width="750px">
+    <el-dialog v-model="dialog.visible" :title="dialog.title" destroy-on-close append-to-bod width="750px">
       <el-form ref="menuFormRef" :model="form" :rules="rules" label-width="100px">
         <el-row>
           <el-col :span="24">
@@ -95,13 +95,13 @@
           <el-col :span="24">
             <el-form-item label="鑿滃崟绫诲瀷" prop="menuType">
               <el-radio-group v-model="form.menuType">
-                <el-radio label="M">鐩綍</el-radio>
-                <el-radio label="C">鑿滃崟</el-radio>
-                <el-radio label="F">鎸夐挳</el-radio>
+                <el-radio value="M">鐩綍</el-radio>
+                <el-radio value="C">鑿滃崟</el-radio>
+                <el-radio value="F">鎸夐挳</el-radio>
               </el-radio-group>
             </el-form-item>
           </el-col>
-          <el-col :span="24" v-if="form.menuType !== 'F'">
+          <el-col v-if="form.menuType !== 'F'" :span="24">
             <el-form-item label="鑿滃崟鍥炬爣" prop="icon">
               <!-- 鍥炬爣閫夋嫨鍣� -->
               <icon-select v-model="form.icon" />
@@ -117,7 +117,7 @@
               <el-input-number v-model="form.orderNum" controls-position="right" :min="0" />
             </el-form-item>
           </el-col>
-          <el-col :span="12" v-if="form.menuType !== 'F'">
+          <el-col v-if="form.menuType !== 'F'" :span="12">
             <el-form-item>
               <template #label>
                 <span>
@@ -134,7 +134,7 @@
               </el-radio-group>
             </el-form-item>
           </el-col>
-          <el-col :span="12" v-if="form.menuType !== 'F'">
+          <el-col v-if="form.menuType !== 'F'" :span="12">
             <el-form-item prop="path">
               <template #label>
                 <span>
@@ -149,7 +149,7 @@
               <el-input v-model="form.path" placeholder="璇疯緭鍏ヨ矾鐢卞湴鍧�" />
             </el-form-item>
           </el-col>
-          <el-col :span="12" v-if="form.menuType === 'C'">
+          <el-col v-if="form.menuType === 'C'" :span="12">
             <el-form-item prop="component">
               <template #label>
                 <span>
@@ -164,7 +164,7 @@
               <el-input v-model="form.component" placeholder="璇疯緭鍏ョ粍浠惰矾寰�" />
             </el-form-item>
           </el-col>
-          <el-col :span="12" v-if="form.menuType !== 'M'">
+          <el-col v-if="form.menuType !== 'M'" :span="12">
             <el-form-item>
               <el-input v-model="form.perms" placeholder="璇疯緭鍏ユ潈闄愭爣璇�" maxlength="100" />
               <template #label>
@@ -179,7 +179,7 @@
               </template>
             </el-form-item>
           </el-col>
-          <el-col :span="12" v-if="form.menuType === 'C'">
+          <el-col v-if="form.menuType === 'C'" :span="12">
             <el-form-item>
               <el-input v-model="form.queryParam" placeholder="璇疯緭鍏ヨ矾鐢卞弬鏁�" maxlength="255" />
               <template #label>
@@ -194,7 +194,7 @@
               </template>
             </el-form-item>
           </el-col>
-          <el-col :span="12" v-if="form.menuType === 'C'">
+          <el-col v-if="form.menuType === 'C'" :span="12">
             <el-form-item>
               <template #label>
                 <span>
@@ -212,7 +212,7 @@
               </el-radio-group>
             </el-form-item>
           </el-col>
-          <el-col :span="12" v-if="form.menuType !== 'F'">
+          <el-col v-if="form.menuType !== 'F'" :span="12">
             <el-form-item>
               <template #label>
                 <span>
@@ -271,14 +271,14 @@
   children: MenuOptionsType[] | undefined;
 }
 
-const { proxy } = getCurrentInstance() as ComponentInternalInstance
-const { sys_show_hide, sys_normal_disable } = toRefs<any>(proxy?.useDict("sys_show_hide", "sys_normal_disable"));
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { sys_show_hide, sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_show_hide', 'sys_normal_disable'));
 
-const menuList = ref<MenuVO[]>([])
-const loading = ref(true)
-const showSearch = ref(true)
-const menuOptions = ref<MenuOptionsType[]>([])
-const isExpandAll = ref(false)
+const menuList = ref<MenuVO[]>([]);
+const loading = ref(true);
+const showSearch = ref(true);
+const menuOptions = ref<MenuOptionsType[]>([]);
+const isExpandAll = ref(false);
 
 const dialog = reactive<DialogOption>({
   visible: false,
@@ -295,11 +295,11 @@
   icon: '',
   menuType: MenuTypeEnum.M,
   orderNum: 1,
-  isFrame: "1",
-  isCache: "0",
-  visible: "0",
-  status: "0"
-}
+  isFrame: '1',
+  isCache: '0',
+  visible: '0',
+  status: '0'
+};
 const data = reactive<PageData<MenuForm, MenuQuery>>({
   form: { ...initFormData },
   queryParams: {
@@ -307,73 +307,73 @@
     status: undefined
   },
   rules: {
-    menuName: [{ required: true, message: "鑿滃崟鍚嶇О涓嶈兘涓虹┖", trigger: "blur" }],
-    orderNum: [{ required: true, message: "鑿滃崟椤哄簭涓嶈兘涓虹┖", trigger: "blur" }],
-    path: [{ required: true, message: "璺敱鍦板潃涓嶈兘涓虹┖", trigger: "blur" }]
-  },
-})
+    menuName: [{ required: true, message: '鑿滃崟鍚嶇О涓嶈兘涓虹┖', trigger: 'blur' }],
+    orderNum: [{ required: true, message: '鑿滃崟椤哄簭涓嶈兘涓虹┖', trigger: 'blur' }],
+    path: [{ required: true, message: '璺敱鍦板潃涓嶈兘涓虹┖', trigger: 'blur' }]
+  }
+});
 
 const menuTableRef = ref<ElTableInstance>();
 
-const { queryParams, form, rules } = toRefs<PageData<MenuForm, MenuQuery>>(data)
+const { queryParams, form, rules } = toRefs<PageData<MenuForm, MenuQuery>>(data);
 /** 鏌ヨ鑿滃崟鍒楄〃 */
 const getList = async () => {
-  loading.value = true
+  loading.value = true;
   const res = await listMenu(queryParams.value);
-  const data = proxy?.handleTree<MenuVO>(res.data, "menuId")
+  const data = proxy?.handleTree<MenuVO>(res.data, 'menuId');
   if (data) {
-    menuList.value = data
+    menuList.value = data;
   }
-  loading.value = false
-}
+  loading.value = false;
+};
 /** 鏌ヨ鑿滃崟涓嬫媺鏍戠粨鏋� */
 const getTreeselect = async () => {
-  menuOptions.value = []
+  menuOptions.value = [];
   const response = await listMenu();
-  const menu: MenuOptionsType = { menuId: 0, menuName: "涓荤被鐩�", children: [] }
-  menu.children = proxy?.handleTree<MenuOptionsType>(response.data, "menuId")
-  menuOptions.value.push(menu)
-}
+  const menu: MenuOptionsType = { menuId: 0, menuName: '涓荤被鐩�', children: [] };
+  menu.children = proxy?.handleTree<MenuOptionsType>(response.data, 'menuId');
+  menuOptions.value.push(menu);
+};
 /** 鍙栨秷鎸夐挳 */
 const cancel = () => {
-  reset()
-  dialog.visible = false
-}
+  reset();
+  dialog.visible = false;
+};
 /** 琛ㄥ崟閲嶇疆 */
 const reset = () => {
   form.value = { ...initFormData };
   menuFormRef.value?.resetFields();
-}
+};
 
 /** 鎼滅储鎸夐挳鎿嶄綔 */
 const handleQuery = () => {
   getList();
-}
+};
 /** 閲嶇疆鎸夐挳鎿嶄綔 */
 const resetQuery = () => {
   queryFormRef.value?.resetFields();
   handleQuery();
-}
+};
 /** 鏂板鎸夐挳鎿嶄綔 */
 const handleAdd = (row?: MenuVO) => {
   reset();
   getTreeselect();
-  row && row.menuId ? form.value.parentId = row.menuId : form.value.parentId = 0;
+  row && row.menuId ? (form.value.parentId = row.menuId) : (form.value.parentId = 0);
   dialog.visible = true;
-  dialog.title = "娣诲姞鑿滃崟";
-}
+  dialog.title = '娣诲姞鑿滃崟';
+};
 /** 灞曞紑/鎶樺彔鎿嶄綔 */
 const handleToggleExpandAll = () => {
   isExpandAll.value = !isExpandAll.value;
-  toggleExpandAll(menuList.value, isExpandAll.value)
-}
+  toggleExpandAll(menuList.value, isExpandAll.value);
+};
 /** 灞曞紑/鎶樺彔鎵�鏈� */
 const toggleExpandAll = (data: MenuVO[], status: boolean) => {
   data.forEach((item: MenuVO) => {
-    menuTableRef.value?.toggleRowExpansion(item, status)
-    if (item.children && item.children.length > 0) toggleExpandAll(item.children, status)
-  })
-}
+    menuTableRef.value?.toggleRowExpansion(item, status);
+    if (item.children && item.children.length > 0) toggleExpandAll(item.children, status);
+  });
+};
 /** 淇敼鎸夐挳鎿嶄綔 */
 const handleUpdate = async (row: MenuVO) => {
   reset();
@@ -383,26 +383,26 @@
     form.value = data;
   }
   dialog.visible = true;
-  dialog.title = "淇敼鑿滃崟";
-}
+  dialog.title = '淇敼鑿滃崟';
+};
 /** 鎻愪氦鎸夐挳 */
 const submitForm = () => {
   menuFormRef.value?.validate(async (valid: boolean) => {
     if (valid) {
       form.value.menuId ? await updateMenu(form.value) : await addMenu(form.value);
-      proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+      proxy?.$modal.msgSuccess('鎿嶄綔鎴愬姛');
       dialog.visible = false;
       await getList();
     }
-  })
-}
+  });
+};
 /** 鍒犻櫎鎸夐挳鎿嶄綔 */
 const handleDelete = async (row: MenuVO) => {
   await proxy?.$modal.confirm('鏄惁纭鍒犻櫎鍚嶇О涓�"' + row.menuName + '"鐨勬暟鎹」?');
   await delMenu(row.menuId);
   await getList();
-  proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-}
+  proxy?.$modal.msgSuccess('鍒犻櫎鎴愬姛');
+};
 
 onMounted(() => {
   getList();
diff --git a/src/views/system/notice/index.vue b/src/views/system/notice/index.vue
index 0c41ce1..7d4bbe0 100644
--- a/src/views/system/notice/index.vue
+++ b/src/views/system/notice/index.vue
@@ -1,17 +1,17 @@
 <template>
   <div class="p-2">
     <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
-      <div class="mb-[10px]" v-show="showSearch">
+      <div v-show="showSearch" class="mb-[10px]">
         <el-card shadow="hover">
-          <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
+          <el-form ref="queryFormRef" :model="queryParams" :inline="true">
             <el-form-item label="鍏憡鏍囬" prop="noticeTitle">
-              <el-input v-model="queryParams.noticeTitle" placeholder="璇疯緭鍏ュ叕鍛婃爣棰�" clearable style="width: 200px" @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.noticeTitle" placeholder="璇疯緭鍏ュ叕鍛婃爣棰�" clearable @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item label="鎿嶄綔浜哄憳" prop="createByName">
-              <el-input v-model="queryParams.createByName" placeholder="璇疯緭鍏ユ搷浣滀汉鍛�" clearable style="width: 200px" @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.createByName" placeholder="璇疯緭鍏ユ搷浣滀汉鍛�" clearable @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item label="绫诲瀷" prop="noticeType">
-              <el-select v-model="queryParams.noticeType" placeholder="鍏憡绫诲瀷" clearable style="width: 200px">
+              <el-select v-model="queryParams.noticeType" placeholder="鍏憡绫诲瀷" clearable>
                 <el-option v-for="dict in sys_notice_type" :key="dict.value" :label="dict.label" :value="dict.value" />
               </el-select>
             </el-form-item>
@@ -28,25 +28,25 @@
       <template #header>
         <el-row :gutter="10" class="mb8">
           <el-col :span="1.5">
-            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:notice:add']">鏂板</el-button>
+            <el-button v-hasPermi="['system:notice:add']" type="primary" plain icon="Plus" @click="handleAdd">鏂板</el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:notice:edit']"
+            <el-button v-hasPermi="['system:notice:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()"
               >淇敼</el-button
             >
           </el-col>
           <el-col :span="1.5">
-            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:notice:remove']">
+            <el-button v-hasPermi="['system:notice:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">
               鍒犻櫎
             </el-button>
           </el-col>
-          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+          <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
         </el-row>
       </template>
 
       <el-table v-loading="loading" :data="noticeList" @selection-change="handleSelectionChange">
         <el-table-column type="selection" width="55" align="center" />
-        <el-table-column label="搴忓彿" align="center" prop="noticeId" width="100" v-if="false" />
+        <el-table-column v-if="false" label="搴忓彿" align="center" prop="noticeId" width="100" />
         <el-table-column label="鍏憡鏍囬" align="center" prop="noticeTitle" :show-overflow-tooltip="true" />
         <el-table-column label="鍏憡绫诲瀷" align="center" prop="noticeType" width="100">
           <template #default="scope">
@@ -67,19 +67,19 @@
         <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="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:notice:edit']"></el-button>
+              <el-button v-hasPermi="['system:notice:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
             </el-tooltip>
             <el-tooltip content="鍒犻櫎" placement="top">
-              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:notice:remove']"></el-button>
+              <el-button v-hasPermi="['system:notice:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></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" />
+      <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
     </el-card>
     <!-- 娣诲姞鎴栦慨鏀瑰叕鍛婂璇濇 -->
-    <el-dialog :title="dialog.title" v-model="dialog.visible" width="780px" append-to-body>
+    <el-dialog v-model="dialog.visible" :title="dialog.title" width="780px" append-to-body>
       <el-form ref="noticeFormRef" :model="form" :rules="rules" label-width="80px">
         <el-row>
           <el-col :span="12">
@@ -97,8 +97,7 @@
           <el-col :span="24">
             <el-form-item label="鐘舵��">
               <el-radio-group v-model="form.status">
-                <el-radio v-for="dict in sys_notice_status" :key="dict.value" :label="dict.value">{{ dict.label
-                }}</el-radio>
+                <el-radio v-for="dict in sys_notice_status" :key="dict.value" :value="dict.value">{{ dict.label }}</el-radio>
               </el-radio-group>
             </el-form-item>
           </el-col>
@@ -120,11 +119,11 @@
 </template>
 
 <script setup name="Notice" lang="ts">
-import { listNotice, getNotice, delNotice, addNotice, updateNotice } from "@/api/system/notice";
-import { NoticeForm, NoticeQuery, NoticeVO } from "@/api/system/notice/types";
+import { listNotice, getNotice, delNotice, addNotice, updateNotice } from '@/api/system/notice';
+import { NoticeForm, NoticeQuery, NoticeVO } from '@/api/system/notice/types';
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
-const { sys_notice_status, sys_notice_type } = toRefs<any>(proxy?.useDict("sys_notice_status", "sys_notice_type"));
+const { sys_notice_status, sys_notice_type } = toRefs<any>(proxy?.useDict('sys_notice_status', 'sys_notice_type'));
 
 const noticeList = ref<NoticeVO[]>([]);
 const loading = ref(true);
@@ -137,7 +136,6 @@
 const queryFormRef = ref<ElFormInstance>();
 const noticeFormRef = ref<ElFormInstance>();
 
-
 const dialog = reactive<DialogOption>({
   visible: false,
   title: ''
@@ -148,10 +146,10 @@
   noticeTitle: '',
   noticeType: '',
   noticeContent: '',
-  status: "0",
+  status: '0',
   remark: '',
   createByName: ''
-}
+};
 const data = reactive<PageData<NoticeForm, NoticeQuery>>({
   form: { ...initFormData },
   queryParams: {
@@ -163,9 +161,9 @@
     noticeType: ''
   },
   rules: {
-    noticeTitle: [{ required: true, message: "鍏憡鏍囬涓嶈兘涓虹┖", trigger: "blur" }],
-    noticeType: [{ required: true, message: "鍏憡绫诲瀷涓嶈兘涓虹┖", trigger: "change" }]
-  },
+    noticeTitle: [{ required: true, message: '鍏憡鏍囬涓嶈兘涓虹┖', trigger: 'blur' }],
+    noticeType: [{ required: true, message: '鍏憡绫诲瀷涓嶈兘涓虹┖', trigger: 'change' }]
+  }
 });
 
 const { queryParams, form, rules } = toRefs(data);
@@ -177,39 +175,39 @@
   noticeList.value = res.rows;
   total.value = res.total;
   loading.value = false;
-}
+};
 /** 鍙栨秷鎸夐挳 */
 const cancel = () => {
   reset();
   dialog.visible = false;
-}
+};
 /** 琛ㄥ崟閲嶇疆 */
 const reset = () => {
   form.value = { ...initFormData };
   noticeFormRef.value?.resetFields();
-}
+};
 /** 鎼滅储鎸夐挳鎿嶄綔 */
 const handleQuery = () => {
   queryParams.value.pageNum = 1;
   getList();
-}
+};
 /** 閲嶇疆鎸夐挳鎿嶄綔 */
 const resetQuery = () => {
   queryFormRef.value?.resetFields();
   handleQuery();
-}
+};
 /** 澶氶�夋閫変腑鏁版嵁 */
 const handleSelectionChange = (selection: NoticeVO[]) => {
-  ids.value = selection.map(item => item.noticeId);
+  ids.value = selection.map((item) => item.noticeId);
   single.value = selection.length != 1;
   multiple.value = !selection.length;
-}
+};
 /** 鏂板鎸夐挳鎿嶄綔 */
 const handleAdd = () => {
   reset();
   dialog.visible = true;
-  dialog.title = "娣诲姞鍏憡";
-}
+  dialog.title = '娣诲姞鍏憡';
+};
 /**淇敼鎸夐挳鎿嶄綔 */
 const handleUpdate = async (row?: NoticeVO) => {
   reset();
@@ -217,29 +215,29 @@
   const { data } = await getNotice(noticeId);
   Object.assign(form.value, data);
   dialog.visible = true;
-  dialog.title = "淇敼鍏憡";
-}
+  dialog.title = '淇敼鍏憡';
+};
 /** 鎻愪氦鎸夐挳 */
 const submitForm = () => {
   noticeFormRef.value?.validate(async (valid: boolean) => {
     if (valid) {
       form.value.noticeId ? await updateNotice(form.value) : await addNotice(form.value);
-      proxy?.$modal.msgSuccess("淇敼鎴愬姛");
+      proxy?.$modal.msgSuccess('鎿嶄綔鎴愬姛');
       dialog.visible = false;
       await getList();
     }
   });
-}
+};
 /** 鍒犻櫎鎸夐挳鎿嶄綔 */
 const handleDelete = async (row?: NoticeVO) => {
-  const noticeIds = row?.noticeId || ids.value
+  const noticeIds = row?.noticeId || ids.value;
   await proxy?.$modal.confirm('鏄惁纭鍒犻櫎鍏憡缂栧彿涓�"' + noticeIds + '"鐨勬暟鎹」锛�');
   await delNotice(noticeIds);
   await getList();
-  proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-}
+  proxy?.$modal.msgSuccess('鍒犻櫎鎴愬姛');
+};
 
 onMounted(() => {
   getList();
-})
+});
 </script>
diff --git a/src/views/system/oss/config.vue b/src/views/system/oss/config.vue
index 876347a..41766f6 100644
--- a/src/views/system/oss/config.vue
+++ b/src/views/system/oss/config.vue
@@ -1,17 +1,17 @@
 <template>
   <div class="p-2">
     <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
-      <div class="mb-[10px]" v-show="showSearch">
+      <div v-show="showSearch" class="mb-[10px]">
         <el-card shadow="hover">
-          <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
+          <el-form ref="queryFormRef" :model="queryParams" :inline="true">
             <el-form-item label="閰嶇疆key" prop="configKey">
-              <el-input v-model="queryParams.configKey" placeholder="閰嶇疆key" clearable style="width: 200px" @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.configKey" placeholder="閰嶇疆key" clearable @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item label="妗跺悕绉�" prop="bucketName">
-              <el-input v-model="queryParams.bucketName" placeholder="璇疯緭鍏ユ《鍚嶇О" clearable style="width: 200px" @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.bucketName" placeholder="璇疯緭鍏ユ《鍚嶇О" clearable @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item label="鏄惁榛樿" prop="status">
-              <el-select v-model="queryParams.status" placeholder="璇烽�夋嫨鐘舵��" clearable style="width: 200px">
+              <el-select v-model="queryParams.status" placeholder="璇烽�夋嫨鐘舵��" clearable>
                 <el-option key="0" label="鏄�" value="0" />
                 <el-option key="1" label="鍚�" value="1" />
               </el-select>
@@ -29,37 +29,39 @@
       <template #header>
         <el-row :gutter="10" class="mb8">
           <el-col :span="1.5">
-            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:ossConfig:add']">鏂板</el-button>
+            <el-button v-hasPermi="['system:ossConfig:add']" type="primary" plain icon="Plus" @click="handleAdd">鏂板</el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:ossConfig:edit']">淇敼</el-button>
+            <el-button v-hasPermi="['system:ossConfig:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()"
+              >淇敼</el-button
+            >
           </el-col>
           <el-col :span="1.5">
-            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:ossConfig:remove']">
+            <el-button v-hasPermi="['system:ossConfig:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">
               鍒犻櫎
             </el-button>
           </el-col>
-          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+          <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
         </el-row>
       </template>
 
       <el-table v-loading="loading" :data="ossConfigList" @selection-change="handleSelectionChange">
         <el-table-column type="selection" width="55" align="center" />
-        <el-table-column label="涓诲缓" align="center" prop="ossConfigId" v-if="columns[0].visible" />
-        <el-table-column label="閰嶇疆key" align="center" prop="configKey" v-if="columns[1].visible" />
-        <el-table-column label="璁块棶绔欑偣" align="center" prop="endpoint" v-if="columns[2].visible" width="200" />
-        <el-table-column label="鑷畾涔夊煙鍚�" align="center" prop="domain" v-if="columns[3].visible" width="200" />
-        <el-table-column label="妗跺悕绉�" align="center" prop="bucketName" v-if="columns[4].visible" />
-        <el-table-column label="鍓嶇紑" align="center" prop="prefix" v-if="columns[5].visible" />
-        <el-table-column label="鍩�" align="center" prop="region" v-if="columns[6].visible" />
-        <el-table-column label="妗舵潈闄愮被鍨�" align="center" prop="accessPolicy" v-if="columns[7].visible">
+        <el-table-column v-if="columns[0].visible" label="涓诲缓" align="center" prop="ossConfigId" />
+        <el-table-column v-if="columns[1].visible" label="閰嶇疆key" align="center" prop="configKey" />
+        <el-table-column v-if="columns[2].visible" label="璁块棶绔欑偣" align="center" prop="endpoint" width="200" />
+        <el-table-column v-if="columns[3].visible" label="鑷畾涔夊煙鍚�" align="center" prop="domain" width="200" />
+        <el-table-column v-if="columns[4].visible" label="妗跺悕绉�" align="center" prop="bucketName" />
+        <el-table-column v-if="columns[5].visible" label="鍓嶇紑" align="center" prop="prefix" />
+        <el-table-column v-if="columns[6].visible" label="鍩�" align="center" prop="region" />
+        <el-table-column v-if="columns[7].visible" label="妗舵潈闄愮被鍨�" align="center" prop="accessPolicy">
           <template #default="scope">
-            <el-tag type="warning" v-if="scope.row.accessPolicy === '0'">private</el-tag>
-            <el-tag type="success" v-if="scope.row.accessPolicy === '1'">public</el-tag>
-            <el-tag type="info" v-if="scope.row.accessPolicy === '2'">custom</el-tag>
+            <el-tag v-if="scope.row.accessPolicy === '0'" type="warning">private</el-tag>
+            <el-tag v-if="scope.row.accessPolicy === '1'" type="success">public</el-tag>
+            <el-tag v-if="scope.row.accessPolicy === '2'" type="info">custom</el-tag>
           </template>
         </el-table-column>
-        <el-table-column label="鏄惁榛樿" align="center" prop="status" v-if="columns[8].visible">
+        <el-table-column v-if="columns[8].visible" label="鏄惁榛樿" align="center" prop="status">
           <template #default="scope">
             <el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch>
           </template>
@@ -67,19 +69,19 @@
         <el-table-column label="鎿嶄綔" fixed="right" align="center" width="150" class-name="small-padding">
           <template #default="scope">
             <el-tooltip content="淇敼" placement="top">
-              <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:ossConfig:edit']"></el-button>
+              <el-button v-hasPermi="['system:ossConfig:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
             </el-tooltip>
             <el-tooltip content="鍒犻櫎" placement="top">
-              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:ossConfig:remove']"></el-button>
+              <el-button v-hasPermi="['system:ossConfig:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></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" />
+      <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
     </el-card>
     <!-- 娣诲姞鎴栦慨鏀瑰璞″瓨鍌ㄩ厤缃璇濇 -->
-    <el-dialog :title="dialog.title" v-model="dialog.visible" width="800px" append-to-body>
+    <el-dialog v-model="dialog.visible" :title="dialog.title" width="800px" append-to-body>
       <el-form ref="ossConfigFormRef" :model="form" :rules="rules" label-width="120px">
         <el-form-item label="閰嶇疆key" prop="configKey">
           <el-input v-model="form.configKey" placeholder="璇疯緭鍏ラ厤缃甼ey" />
@@ -104,14 +106,14 @@
         </el-form-item>
         <el-form-item label="鏄惁HTTPS">
           <el-radio-group v-model="form.isHttps">
-            <el-radio v-for="dict in sys_yes_no" :key="dict.value" :label="dict.value">{{ dict.label }}</el-radio>
+            <el-radio v-for="dict in sys_yes_no" :key="dict.value" :value="dict.value">{{ dict.label }}</el-radio>
           </el-radio-group>
         </el-form-item>
         <el-form-item label="妗舵潈闄愮被鍨�">
           <el-radio-group v-model="form.accessPolicy">
-            <el-radio label="0">private</el-radio>
-            <el-radio label="1">public</el-radio>
-            <el-radio label="2">custom</el-radio>
+            <el-radio value="0">private</el-radio>
+            <el-radio value="1">public</el-radio>
+            <el-radio value="2">custom</el-radio>
           </el-radio-group>
         </el-form-item>
         <el-form-item label="鍩�" prop="region">
@@ -132,19 +134,11 @@
 </template>
 
 <script setup name="OssConfig" lang="ts">
-import {
-  listOssConfig,
-  getOssConfig,
-  delOssConfig,
-  addOssConfig,
-  updateOssConfig,
-  changeOssConfigStatus
-} from "@/api/system/ossConfig";
-import { OssConfigForm, OssConfigQuery, OssConfigVO } from "@/api/system/ossConfig/types";
+import { listOssConfig, getOssConfig, delOssConfig, addOssConfig, updateOssConfig, changeOssConfigStatus } from '@/api/system/ossConfig';
+import { OssConfigForm, OssConfigQuery, OssConfigVO } from '@/api/system/ossConfig/types';
 
-
-const { proxy } = getCurrentInstance() as ComponentInternalInstance
-const { sys_yes_no } = toRefs<any>(proxy?.useDict("sys_yes_no"));
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { sys_yes_no } = toRefs<any>(proxy?.useDict('sys_yes_no'));
 
 const ossConfigList = ref<OssConfigVO[]>([]);
 const buttonLoading = ref(false);
@@ -176,7 +170,6 @@
   { key: 8, label: `鐘舵�乣, visible: true }
 ]);
 
-
 const initFormData: OssConfigForm = {
   ossConfigId: undefined,
   configKey: '',
@@ -186,12 +179,12 @@
   prefix: '',
   endpoint: '',
   domain: '',
-  isHttps: "N",
-  accessPolicy: "1",
+  isHttps: 'N',
+  accessPolicy: '1',
   region: '',
-  status: "1",
-  remark: '',
-}
+  status: '1',
+  remark: ''
+};
 const data = reactive<PageData<OssConfigForm, OssConfigQuery>>({
   form: { ...initFormData },
   // 鏌ヨ鍙傛暟
@@ -200,47 +193,47 @@
     pageSize: 10,
     configKey: '',
     bucketName: '',
-    status: '',
+    status: ''
   },
   rules: {
-    configKey: [{ required: true, message: "configKey涓嶈兘涓虹┖", trigger: "blur" },],
+    configKey: [{ required: true, message: 'configKey涓嶈兘涓虹┖', trigger: 'blur' }],
     accessKey: [
-      { required: true, message: "accessKey涓嶈兘涓虹┖", trigger: "blur" },
+      { required: true, message: 'accessKey涓嶈兘涓虹┖', trigger: 'blur' },
       {
         min: 2,
         max: 200,
-        message: "accessKey闀垮害蹇呴』浠嬩簬 2 鍜� 100 涔嬮棿",
-        trigger: "blur",
-      },
+        message: 'accessKey闀垮害蹇呴』浠嬩簬 2 鍜� 100 涔嬮棿',
+        trigger: 'blur'
+      }
     ],
     secretKey: [
-      { required: true, message: "secretKey涓嶈兘涓虹┖", trigger: "blur" },
+      { required: true, message: 'secretKey涓嶈兘涓虹┖', trigger: 'blur' },
       {
         min: 2,
         max: 100,
-        message: "secretKey闀垮害蹇呴』浠嬩簬 2 鍜� 100 涔嬮棿",
-        trigger: "blur",
-      },
+        message: 'secretKey闀垮害蹇呴』浠嬩簬 2 鍜� 100 涔嬮棿',
+        trigger: 'blur'
+      }
     ],
     bucketName: [
-      { required: true, message: "bucketName涓嶈兘涓虹┖", trigger: "blur" },
+      { required: true, message: 'bucketName涓嶈兘涓虹┖', trigger: 'blur' },
       {
         min: 2,
         max: 100,
-        message: "bucketName闀垮害蹇呴』浠嬩簬 2 鍜� 100 涔嬮棿",
-        trigger: "blur",
-      },
+        message: 'bucketName闀垮害蹇呴』浠嬩簬 2 鍜� 100 涔嬮棿',
+        trigger: 'blur'
+      }
     ],
     endpoint: [
-      { required: true, message: "endpoint涓嶈兘涓虹┖", trigger: "blur" },
+      { required: true, message: 'endpoint涓嶈兘涓虹┖', trigger: 'blur' },
       {
         min: 2,
         max: 100,
-        message: "endpoint鍚嶇О闀垮害蹇呴』浠嬩簬 2 鍜� 100 涔嬮棿",
-        trigger: "blur",
-      },
+        message: 'endpoint鍚嶇О闀垮害蹇呴』浠嬩簬 2 鍜� 100 涔嬮棿',
+        trigger: 'blur'
+      }
     ],
-    accessPolicy: [{ required: true, message: "accessPolicy涓嶈兘涓虹┖", trigger: "blur" }]
+    accessPolicy: [{ required: true, message: 'accessPolicy涓嶈兘涓虹┖', trigger: 'blur' }]
   }
 });
 
@@ -253,39 +246,39 @@
   ossConfigList.value = res.rows;
   total.value = res.total;
   loading.value = false;
-}
+};
 /** 鍙栨秷鎸夐挳 */
 const cancel = () => {
   dialog.visible = false;
   reset();
-}
+};
 /** 琛ㄥ崟閲嶇疆 */
 const reset = () => {
   form.value = { ...initFormData };
   ossConfigFormRef.value?.resetFields();
-}
+};
 /** 鎼滅储鎸夐挳鎿嶄綔 */
 const handleQuery = () => {
   queryParams.value.pageNum = 1;
   getList();
-}
+};
 /** 閲嶇疆鎸夐挳鎿嶄綔 */
 const resetQuery = () => {
   queryFormRef.value?.resetFields();
   handleQuery();
-}
+};
 /** 閫夋嫨鏉℃暟  */
 const handleSelectionChange = (selection: OssConfigVO[]) => {
-  ids.value = selection.map(item => item.ossConfigId);
+  ids.value = selection.map((item) => item.ossConfigId);
   single.value = selection.length != 1;
   multiple.value = !selection.length;
-}
+};
 /** 鏂板鎸夐挳鎿嶄綔 */
 const handleAdd = () => {
   reset();
   dialog.visible = true;
-  dialog.title = "娣诲姞瀵硅薄瀛樺偍閰嶇疆";
-}
+  dialog.title = '娣诲姞瀵硅薄瀛樺偍閰嶇疆';
+};
 /** 淇敼鎸夐挳鎿嶄綔 */
 const handleUpdate = async (row?: OssConfigVO) => {
   reset();
@@ -293,49 +286,49 @@
   const res = await getOssConfig(ossConfigId);
   Object.assign(form.value, res.data);
   dialog.visible = true;
-  dialog.title = "淇敼瀵硅薄瀛樺偍閰嶇疆";
-}
+  dialog.title = '淇敼瀵硅薄瀛樺偍閰嶇疆';
+};
 /** 鎻愪氦鎸夐挳 */
 const submitForm = () => {
   ossConfigFormRef.value?.validate(async (valid: boolean) => {
     if (valid) {
       buttonLoading.value = true;
       if (form.value.ossConfigId) {
-        await updateOssConfig(form.value).finally(() => buttonLoading.value = false);
+        await updateOssConfig(form.value).finally(() => (buttonLoading.value = false));
       } else {
-        await addOssConfig(form.value).finally(() => buttonLoading.value = false);
+        await addOssConfig(form.value).finally(() => (buttonLoading.value = false));
       }
-      proxy?.$modal.msgSuccess("鏂板鎴愬姛");
+      proxy?.$modal.msgSuccess('鏂板鎴愬姛');
       dialog.visible = false;
       await getList();
     }
   });
-}
+};
 /** 鐘舵�佷慨鏀�  */
 const handleStatusChange = async (row: OssConfigVO) => {
-  let text = row.status === "0" ? "鍚敤" : "鍋滅敤";
+  let text = row.status === '0' ? '鍚敤' : '鍋滅敤';
   try {
     await proxy?.$modal.confirm('纭瑕�"' + text + '""' + row.configKey + '"閰嶇疆鍚�?');
     await changeOssConfigStatus(row.ossConfigId, row.status, row.configKey);
-    await getList()
-    proxy?.$modal.msgSuccess(text + "鎴愬姛");
-  } catch { return } finally {
-    row.status = row.status === "0" ? "1" : "0";
+    await getList();
+    proxy?.$modal.msgSuccess(text + '鎴愬姛');
+  } catch {
+    return;
+  } finally {
+    row.status = row.status === '0' ? '1' : '0';
   }
-
-}
+};
 /** 鍒犻櫎鎸夐挳鎿嶄綔 */
 const handleDelete = async (row?: OssConfigVO) => {
   const ossConfigIds = row?.ossConfigId || ids.value;
   await proxy?.$modal.confirm('鏄惁纭鍒犻櫎OSS閰嶇疆缂栧彿涓�"' + ossConfigIds + '"鐨勬暟鎹」?');
   loading.value = true;
-  await delOssConfig(ossConfigIds).finally(() => loading.value = false);
+  await delOssConfig(ossConfigIds).finally(() => (loading.value = false));
   await getList();
-  proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-
-}
+  proxy?.$modal.msgSuccess('鍒犻櫎鎴愬姛');
+};
 
 onMounted(() => {
   getList();
-})
+});
 </script>
diff --git a/src/views/system/oss/index.vue b/src/views/system/oss/index.vue
index ec11708..a2f1f1b 100644
--- a/src/views/system/oss/index.vue
+++ b/src/views/system/oss/index.vue
@@ -1,19 +1,19 @@
 <template>
   <div class="p-2">
     <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
-      <div class="mb-[10px]" v-show="showSearch">
+      <div v-show="showSearch" class="mb-[10px]">
         <el-card shadow="hover">
-          <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
+          <el-form ref="queryFormRef" :model="queryParams" :inline="true">
             <el-form-item label="鏂囦欢鍚�" prop="fileName">
-              <el-input v-model="queryParams.fileName" placeholder="璇疯緭鍏ユ枃浠跺悕" clearable style="width: 200px" @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.fileName" placeholder="璇疯緭鍏ユ枃浠跺悕" clearable @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item label="鍘熷悕" prop="originalName">
-              <el-input v-model="queryParams.originalName" placeholder="璇疯緭鍏ュ師鍚�" clearable style="width: 200px" @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.originalName" placeholder="璇疯緭鍏ュ師鍚�" clearable @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item label="鏂囦欢鍚庣紑" prop="fileSuffix">
-              <el-input v-model="queryParams.fileSuffix" placeholder="璇疯緭鍏ユ枃浠跺悗缂�" clearable style="width: 200px" @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.fileSuffix" placeholder="璇疯緭鍏ユ枃浠跺悗缂�" clearable @keyup.enter="handleQuery" />
             </el-form-item>
-            <el-form-item label="鍒涘缓鏃堕棿">
+            <el-form-item label="鍒涘缓鏃堕棿" style="width: 308px;">
               <el-date-picker
                 v-model="dateRangeCreateTime"
                 value-format="YYYY-MM-DD HH:mm:ss"
@@ -25,7 +25,7 @@
               ></el-date-picker>
             </el-form-item>
             <el-form-item label="鏈嶅姟鍟�" prop="service">
-              <el-input v-model="queryParams.service" placeholder="璇疯緭鍏ユ湇鍔″晢" clearable style="width: 200px" @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.service" placeholder="璇疯緭鍏ユ湇鍔″晢" clearable @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item>
               <el-button type="primary" icon="search" @click="handleQuery">鎼滅储</el-button>
@@ -40,44 +40,42 @@
       <template #header>
         <el-row :gutter="10" class="mb8">
           <el-col :span="1.5">
-            <el-button type="primary" plain icon="Upload" @click="handleFile" v-hasPermi="['system:oss:upload']">涓婁紶鏂囦欢</el-button>
+            <el-button v-hasPermi="['system:oss:upload']" type="primary" plain icon="Upload" @click="handleFile">涓婁紶鏂囦欢</el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="primary" plain icon="Upload" @click="handleImage" v-hasPermi="['system:oss:upload']">涓婁紶鍥剧墖</el-button>
+            <el-button v-hasPermi="['system:oss:upload']" type="primary" plain icon="Upload" @click="handleImage">涓婁紶鍥剧墖</el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:oss:remove']">
+            <el-button v-hasPermi="['system:oss:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">
               鍒犻櫎
             </el-button>
           </el-col>
           <el-col :span="1.5">
             <el-button
+              v-hasPermi="['system:oss:edit']"
               :type="previewListResource ? 'danger' : 'warning'"
               plain
               @click="handlePreviewListResource(!previewListResource)"
-              v-hasPermi="['system:oss:edit']"
-              >棰勮寮�鍏� :
-              {{
-                previewListResource ? "绂佺敤" : "鍚敤" }}</el-button
+              >棰勮寮�鍏� : {{ previewListResource ? '绂佺敤' : '鍚敤' }}</el-button
             >
           </el-col>
           <el-col :span="1.5">
-            <el-button type="info" plain icon="Operation" @click="handleOssConfig" v-hasPermi="['system:oss:list']">閰嶇疆绠$悊</el-button>
+            <el-button v-hasPermi="['system:oss:list']" type="info" plain icon="Operation" @click="handleOssConfig">閰嶇疆绠$悊</el-button>
           </el-col>
-          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+          <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
         </el-row>
       </template>
 
       <el-table
+        v-if="showTable"
         v-loading="loading"
         :data="ossList"
-        @selection-change="handleSelectionChange"
         :header-cell-class-name="handleHeaderClass"
+        @selection-change="handleSelectionChange"
         @header-click="handleHeaderCLick"
-        v-if="showTable"
       >
         <el-table-column type="selection" width="55" align="center" />
-        <el-table-column label="瀵硅薄瀛樺偍涓婚敭" align="center" prop="ossId" v-if="false" />
+        <el-table-column v-if="false" label="瀵硅薄瀛樺偍涓婚敭" align="center" prop="ossId" />
         <el-table-column label="鏂囦欢鍚�" align="center" prop="fileName" />
         <el-table-column label="鍘熷悕" align="center" prop="originalName" />
         <el-table-column label="鏂囦欢鍚庣紑" align="center" prop="fileSuffix" />
@@ -90,7 +88,7 @@
               :src="scope.row.url"
               :preview-src-list="[scope.row.url]"
             />
-            <span v-text="scope.row.url" v-if="!checkFileSuffix(scope.row.fileSuffix) || !previewListResource" />
+            <span v-if="!checkFileSuffix(scope.row.fileSuffix) || !previewListResource" v-text="scope.row.url" />
           </template>
         </el-table-column>
         <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="180" sortable="custom">
@@ -103,23 +101,23 @@
         <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="Download" @click="handleDownload(scope.row)" v-hasPermi="['system:oss:download']"></el-button>
+              <el-button v-hasPermi="['system:oss:download']" link type="primary" icon="Download" @click="handleDownload(scope.row)"></el-button>
             </el-tooltip>
             <el-tooltip content="鍒犻櫎" placement="top">
-              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:oss:remove']"></el-button>
+              <el-button v-hasPermi="['system:oss:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></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" />
+      <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
     </el-card>
     <!-- 娣诲姞鎴栦慨鏀筄SS瀵硅薄瀛樺偍瀵硅瘽妗� -->
-    <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+    <el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
       <el-form ref="ossFormRef" :model="form" :rules="rules" label-width="80px">
         <el-form-item label="鏂囦欢鍚�">
-          <fileUpload v-model="form.file" v-if="type === 0" />
-          <imageUpload v-model="form.file" v-if="type === 1" />
+          <fileUpload v-if="type === 0" v-model="form.file" />
+          <imageUpload v-if="type === 1" v-model="form.file" />
         </el-form-item>
       </el-form>
       <template #footer>
@@ -133,9 +131,9 @@
 </template>
 
 <script setup name="Oss" lang="ts">
-import { listOss, delOss } from "@/api/system/oss";
-import ImagePreview from "@/components/ImagePreview/index.vue";
-import { OssForm, OssQuery, OssVO } from "@/api/system/oss/types";
+import { listOss, delOss } from '@/api/system/oss';
+import ImagePreview from '@/components/ImagePreview/index.vue';
+import { OssForm, OssQuery, OssVO } from '@/api/system/oss/types';
 
 const router = useRouter();
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@@ -165,8 +163,8 @@
 const queryFormRef = ref<ElFormInstance>();
 
 const initFormData = {
-  file: undefined,
-}
+  file: undefined
+};
 const data = reactive<PageData<OssForm, OssQuery>>({
   form: { ...initFormData },
   // 鏌ヨ鍙傛暟
@@ -182,9 +180,7 @@
     isAsc: defaultSort.value.order
   },
   rules: {
-    file: [
-      { required: true, message: "鏂囦欢涓嶈兘涓虹┖", trigger: "blur" }
-    ]
+    file: [{ required: true, message: '鏂囦欢涓嶈兘涓虹┖', trigger: 'blur' }]
   }
 });
 
@@ -193,19 +189,18 @@
 /** 鏌ヨOSS瀵硅薄瀛樺偍鍒楄〃 */
 const getList = async () => {
   loading.value = true;
-  const res = await proxy?.getConfigKey("sys.oss.previewListResource");
+  const res = await proxy?.getConfigKey('sys.oss.previewListResource');
   previewListResource.value = res?.data === undefined ? true : res.data === 'true';
-  const response = await listOss(proxy?.addDateRange(queryParams.value, dateRangeCreateTime.value, "CreateTime"));
+  const response = await listOss(proxy?.addDateRange(queryParams.value, dateRangeCreateTime.value, 'CreateTime'));
   ossList.value = response.rows;
   total.value = response.total;
   loading.value = false;
   showTable.value = true;
-}
-function checkFileSuffix(fileSuffix: string[]) {
-  let arr = ["png", "jpg", "jpeg"];
-  return arr.some(type => {
-    return fileSuffix.indexOf(type) > -1;
-  });
+};
+function checkFileSuffix(fileSuffix: string | string[]) {
+  const arr = [".png", ".jpg", ".jpeg"];
+  const suffixArray = Array.isArray(fileSuffix) ? fileSuffix : [fileSuffix];
+  return suffixArray.some(suffix => arr.includes(suffix.toLowerCase()));
 }
 /** 鍙栨秷鎸夐挳 */
 function cancel() {
@@ -233,18 +228,18 @@
 }
 /** 閫夋嫨鏉℃暟  */
 function handleSelectionChange(selection: OssVO[]) {
-  ids.value = selection.map(item => item.ossId);
+  ids.value = selection.map((item) => item.ossId);
   single.value = selection.length != 1;
   multiple.value = !selection.length;
 }
 /** 璁剧疆鍒楃殑鎺掑簭涓烘垜浠嚜瀹氫箟鐨勬帓搴� */
 const handleHeaderClass = ({ column }: any): any => {
-  column.order = column.multiOrder
-}
+  column.order = column.multiOrder;
+};
 /** 鐐瑰嚮琛ㄥご杩涜鎺掑簭 */
 const handleHeaderCLick = (column: any) => {
   if (column.sortable !== 'custom') {
-    return
+    return;
   }
   switch (column.multiOrder) {
     case 'descending':
@@ -257,20 +252,20 @@
       column.multiOrder = 'descending';
       break;
   }
-  handleOrderChange(column.property, column.multiOrder)
-}
+  handleOrderChange(column.property, column.multiOrder);
+};
 const handleOrderChange = (prop: string, order: string) => {
-  let orderByArr = queryParams.value.orderByColumn ? queryParams.value.orderByColumn.split(",") : [];
-  let isAscArr = queryParams.value.isAsc ? queryParams.value.isAsc.split(",") : [];
-  let propIndex = orderByArr.indexOf(prop)
+  let orderByArr = queryParams.value.orderByColumn ? queryParams.value.orderByColumn.split(',') : [];
+  let isAscArr = queryParams.value.isAsc ? queryParams.value.isAsc.split(',') : [];
+  let propIndex = orderByArr.indexOf(prop);
   if (propIndex !== -1) {
     if (order) {
       //鎺掑簭閲屽凡瀛樺湪 鍙慨鏀规帓搴�
       isAscArr[propIndex] = order;
     } else {
       //濡傛灉order涓簄ull 鍒欏垹闄ゆ帓搴忓瓧娈靛拰灞炴��
-      isAscArr.splice(propIndex, 1);//鍒犻櫎鎺掑簭
-      orderByArr.splice(propIndex, 1);//鍒犻櫎灞炴��
+      isAscArr.splice(propIndex, 1); //鍒犻櫎鎺掑簭
+      orderByArr.splice(propIndex, 1); //鍒犻櫎灞炴��
     }
   } else {
     //鎺掑簭閲屼笉瀛樺湪鍒欐柊澧炴帓搴�
@@ -278,58 +273,60 @@
     isAscArr.push(order);
   }
   //鍚堝苟鎺掑簭
-  queryParams.value.orderByColumn = orderByArr.join(",");
-  queryParams.value.isAsc = isAscArr.join(",");
+  queryParams.value.orderByColumn = orderByArr.join(',');
+  queryParams.value.isAsc = isAscArr.join(',');
   getList();
-}
+};
 /** 浠诲姟鏃ュ織鍒楄〃鏌ヨ */
 const handleOssConfig = () => {
-  router.push('/system/oss-config/index')
-}
+  router.push('/system/oss-config/index');
+};
 /** 鏂囦欢鎸夐挳鎿嶄綔 */
 const handleFile = () => {
   reset();
   type.value = 0;
   dialog.visible = true;
-  dialog.title = "涓婁紶鏂囦欢";
-}
+  dialog.title = '涓婁紶鏂囦欢';
+};
 /** 鍥剧墖鎸夐挳鎿嶄綔 */
 const handleImage = () => {
   reset();
   type.value = 1;
   dialog.visible = true;
-  dialog.title = "涓婁紶鍥剧墖";
-}
+  dialog.title = '涓婁紶鍥剧墖';
+};
 /** 鎻愪氦鎸夐挳 */
 const submitForm = () => {
   dialog.visible = false;
   getList();
-}
+};
 /** 涓嬭浇鎸夐挳鎿嶄綔 */
 const handleDownload = (row: OssVO) => {
-  proxy?.$download.oss(row.ossId)
-}
+  proxy?.$download.oss(row.ossId);
+};
 /** 鐢ㄦ埛鐘舵�佷慨鏀�  */
 const handlePreviewListResource = async (preview: boolean) => {
-  let text = preview ? "鍚敤" : "鍋滅敤";
+  let text = preview ? '鍚敤' : '鍋滅敤';
   try {
     await proxy?.$modal.confirm('纭瑕�"' + text + '""棰勮鍒楄〃鍥剧墖"閰嶇疆鍚�?');
-    await proxy?.updateConfigByKey("sys.oss.previewListResource", preview);
-    await getList()
-    proxy?.$modal.msgSuccess(text + "鎴愬姛");
-  } catch { return }
-}
+    await proxy?.updateConfigByKey('sys.oss.previewListResource', preview);
+    await getList();
+    proxy?.$modal.msgSuccess(text + '鎴愬姛');
+  } catch {
+    return;
+  }
+};
 /** 鍒犻櫎鎸夐挳鎿嶄綔 */
 const handleDelete = async (row?: OssVO) => {
   const ossIds = row?.ossId || ids.value;
   await proxy?.$modal.confirm('鏄惁纭鍒犻櫎OSS瀵硅薄瀛樺偍缂栧彿涓�"' + ossIds + '"鐨勬暟鎹」?');
   loading.value = true;
-  await delOss(ossIds).finally(() => loading.value = false);
+  await delOss(ossIds).finally(() => (loading.value = false));
   await getList();
-  proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-}
+  proxy?.$modal.msgSuccess('鍒犻櫎鎴愬姛');
+};
 
 onMounted(() => {
   getList();
-})
+});
 </script>
diff --git a/src/views/system/post/index.vue b/src/views/system/post/index.vue
index b1cddd3..49b735b 100644
--- a/src/views/system/post/index.vue
+++ b/src/views/system/post/index.vue
@@ -1,117 +1,182 @@
 <template>
   <div class="p-2">
-    <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
-      <div class="mb-[10px]" v-show="showSearch">
+    <el-row :gutter="20">
+      <!-- 閮ㄩ棬鏍� -->
+      <el-col :lg="4" :xs="24" style="">
         <el-card shadow="hover">
-          <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="70">
-            <el-form-item label="宀椾綅缂栫爜" prop="postCode">
-              <el-input v-model="queryParams.postCode" placeholder="璇疯緭鍏ュ矖浣嶇紪鐮�" clearable style="width: 200px" @keyup.enter="handleQuery" />
-            </el-form-item>
+          <el-input v-model="deptName" placeholder="璇疯緭鍏ラ儴闂ㄥ悕绉�" prefix-icon="Search" clearable />
+          <el-tree
+            ref="deptTreeRef"
+            class="mt-2"
+            node-key="id"
+            :data="deptOptions"
+            :props="{ label: 'label', children: 'children' }"
+            :expand-on-click-node="false"
+            :filter-node-method="filterNode"
+            highlight-current
+            default-expand-all
+            @node-click="handleNodeClick"
+          />
+        </el-card>
+      </el-col>
+      <el-col :lg="20" :xs="24">
+        <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+          <div v-show="showSearch" class="mb-[10px]">
+            <el-card shadow="hover">
+              <el-form ref="queryFormRef" :model="queryParams" :inline="true">
+                <el-form-item label="宀椾綅缂栫爜" prop="postCode">
+                  <el-input v-model="queryParams.postCode" placeholder="璇疯緭鍏ュ矖浣嶇紪鐮�" clearable @keyup.enter="handleQuery" />
+                </el-form-item>
+                <el-form-item label="绫诲埆缂栫爜" prop="postCategory">
+                  <el-input
+                    v-model="queryParams.postCategory"
+                    placeholder="璇疯緭鍏ョ被鍒紪鐮�"
+                    clearable
+                    style="width: 200px"
+                    @keyup.enter="handleQuery"
+                  />
+                </el-form-item>
+                <el-form-item label="宀椾綅鍚嶇О" prop="postName">
+                  <el-input v-model="queryParams.postName" placeholder="璇疯緭鍏ュ矖浣嶅悕绉�" clearable @keyup.enter="handleQuery" />
+                </el-form-item>
+                <el-form-item label="閮ㄩ棬" prop="deptId">
+                  <el-tree-select
+                    v-model="queryParams.deptId"
+                    :data="deptOptions"
+                    :props="{ value: 'id', label: 'label', children: 'children' }"
+                    value-key="id"
+                    placeholder="璇烽�夋嫨閮ㄩ棬"
+                    check-strictly
+                  />
+                </el-form-item>
+                <el-form-item label="鐘舵��" prop="status">
+                  <el-select v-model="queryParams.status" placeholder="宀椾綅鐘舵��" clearable>
+                    <el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
+                  </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>
+            </el-card>
+          </div>
+        </transition>
+        <el-card shadow="hover">
+          <template #header>
+            <el-row :gutter="10" class="mb8">
+              <el-col :span="1.5">
+                <el-button v-hasPermi="['system:post:add']" type="primary" plain icon="Plus" @click="handleAdd">鏂板</el-button>
+              </el-col>
+              <el-col :span="1.5">
+                <el-button v-hasPermi="['system:post:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()"
+                  >淇敼</el-button
+                >
+              </el-col>
+              <el-col :span="1.5">
+                <el-button v-hasPermi="['system:post:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">
+                  鍒犻櫎
+                </el-button>
+              </el-col>
+              <el-col :span="1.5">
+                <el-button v-hasPermi="['system:post:export']" type="warning" plain icon="Download" @click="handleExport">瀵煎嚭</el-button>
+              </el-col>
+              <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
+            </el-row>
+          </template>
+          <el-table v-loading="loading" :data="postList" @selection-change="handleSelectionChange">
+            <el-table-column type="selection" width="55" align="center" />
+            <el-table-column v-if="false" label="宀椾綅缂栧彿" align="center" prop="postId" />
+            <el-table-column label="宀椾綅缂栫爜" align="center" prop="postCode" />
+            <el-table-column label="绫诲埆缂栫爜" align="center" prop="postCategory" />
+            <el-table-column label="宀椾綅鍚嶇О" align="center" prop="postName" />
+            <el-table-column label="閮ㄩ棬" align="center" prop="deptName" />
+            <el-table-column label="鎺掑簭" align="center" prop="postSort" />
+            <el-table-column label="鐘舵��" align="center" prop="status">
+              <template #default="scope">
+                <dict-tag :options="sys_normal_disable" :value="scope.row.status" />
+              </template>
+            </el-table-column>
+            <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="180">
+              <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 v-hasPermi="['system:post:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
+                </el-tooltip>
+                <el-tooltip content="鍒犻櫎" placement="top">
+                  <el-button v-hasPermi="['system:post:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
+                </el-tooltip>
+              </template>
+            </el-table-column>
+          </el-table>
+
+          <pagination
+            v-show="total > 0"
+            v-model:page="queryParams.pageNum"
+            v-model:limit="queryParams.pageSize"
+            :total="total"
+            @pagination="getList"
+          />
+        </el-card>
+
+        <!-- 娣诲姞鎴栦慨鏀瑰矖浣嶅璇濇 -->
+        <el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
+          <el-form ref="postFormRef" :model="form" :rules="rules" label-width="80px">
             <el-form-item label="宀椾綅鍚嶇О" prop="postName">
-              <el-input v-model="queryParams.postName" placeholder="璇疯緭鍏ュ矖浣嶅悕绉�" clearable style="width: 200px" @keyup.enter="handleQuery" />
+              <el-input v-model="form.postName" placeholder="璇疯緭鍏ュ矖浣嶅悕绉�" />
             </el-form-item>
-            <el-form-item label="鐘舵��" prop="status">
-              <el-select v-model="queryParams.status" placeholder="宀椾綅鐘舵��" clearable style="width: 200px">
-                <el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
-              </el-select>
+            <el-form-item label="閮ㄩ棬" prop="deptId">
+              <el-tree-select
+                v-model="form.deptId"
+                :data="deptOptions"
+                :props="{ value: 'id', label: 'label', children: 'children' }"
+                value-key="id"
+                placeholder="璇烽�夋嫨閮ㄩ棬"
+                check-strictly
+              />
             </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 label="宀椾綅缂栫爜" prop="postCode">
+              <el-input v-model="form.postCode" placeholder="璇疯緭鍏ョ紪鐮佸悕绉�" />
+            </el-form-item>
+            <el-form-item label="绫诲埆缂栫爜" prop="postCategory">
+              <el-input v-model="form.postCategory" placeholder="璇疯緭鍏ョ被鍒紪鐮�" />
+            </el-form-item>
+            <el-form-item label="宀椾綅椤哄簭" prop="postSort">
+              <el-input-number v-model="form.postSort" controls-position="right" :min="0" />
+            </el-form-item>
+            <el-form-item label="宀椾綅鐘舵��" prop="status">
+              <el-radio-group v-model="form.status">
+                <el-radio v-for="dict in sys_normal_disable" :key="dict.value" :value="dict.value">{{ dict.label }}</el-radio>
+              </el-radio-group>
+            </el-form-item>
+            <el-form-item label="澶囨敞" prop="remark">
+              <el-input v-model="form.remark" type="textarea" placeholder="璇疯緭鍏ュ唴瀹�" />
             </el-form-item>
           </el-form>
-        </el-card>
-      </div>
-    </transition>
-    <el-card shadow="hover">
-      <template #header>
-        <el-row :gutter="10" class="mb8">
-          <el-col :span="1.5">
-            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:post:add']">鏂板</el-button>
-          </el-col>
-          <el-col :span="1.5">
-            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:post:edit']">淇敼</el-button>
-          </el-col>
-          <el-col :span="1.5">
-            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:post:remove']">
-              鍒犻櫎
-            </el-button>
-          </el-col>
-          <el-col :span="1.5">
-            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:post:export']">瀵煎嚭</el-button>
-          </el-col>
-          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
-        </el-row>
-      </template>
-
-      <el-table v-loading="loading" :data="postList" @selection-change="handleSelectionChange">
-        <el-table-column type="selection" width="55" align="center" />
-        <el-table-column label="宀椾綅缂栧彿" align="center" prop="postId" v-if="false" />
-        <el-table-column label="宀椾綅缂栫爜" align="center" prop="postCode" />
-        <el-table-column label="宀椾綅鍚嶇О" align="center" prop="postName" />
-        <el-table-column label="宀椾綅鎺掑簭" align="center" prop="postSort" />
-        <el-table-column label="鐘舵��" align="center" prop="status">
-          <template #default="scope">
-            <dict-tag :options="sys_normal_disable" :value="scope.row.status" />
+          <template #footer>
+            <div class="dialog-footer">
+              <el-button type="primary" @click="submitForm">纭� 瀹�</el-button>
+              <el-button @click="cancel">鍙� 娑�</el-button>
+            </div>
           </template>
-        </el-table-column>
-        <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="180">
-          <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="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:post:edit']"></el-button>
-            </el-tooltip>
-            <el-tooltip content="鍒犻櫎" placement="top">
-              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:post: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="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
-      <el-form ref="postFormRef" :model="form" :rules="rules" label-width="80px">
-        <el-form-item label="宀椾綅鍚嶇О" prop="postName">
-          <el-input v-model="form.postName" placeholder="璇疯緭鍏ュ矖浣嶅悕绉�" />
-        </el-form-item>
-        <el-form-item label="宀椾綅缂栫爜" prop="postCode">
-          <el-input v-model="form.postCode" placeholder="璇疯緭鍏ョ紪鐮佸悕绉�" />
-        </el-form-item>
-        <el-form-item label="宀椾綅椤哄簭" prop="postSort">
-          <el-input-number v-model="form.postSort" controls-position="right" :min="0" />
-        </el-form-item>
-        <el-form-item label="宀椾綅鐘舵��" prop="status">
-          <el-radio-group v-model="form.status">
-            <el-radio v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.value">{{ dict.label }}</el-radio>
-          </el-radio-group>
-        </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>
+        </el-dialog>
+      </el-col>
+    </el-row>
   </div>
 </template>
 
 <script setup name="Post" lang="ts">
-import { listPost, addPost, delPost, getPost, updatePost } from "@/api/system/post";
-import { PostForm, PostQuery, PostVO } from "@/api/system/post/types";
+import { listPost, addPost, delPost, getPost, updatePost } from '@/api/system/post';
+import { PostForm, PostQuery, PostVO } from '@/api/system/post/types';
+import { DeptVO } from '@/api/system/dept/types';
+import api from '@/api/system/user';
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
-const { sys_normal_disable } = toRefs<any>(proxy?.useDict("sys_normal_disable"));
+const { sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_normal_disable'));
 
 const postList = ref<PostVO[]>([]);
 const loading = ref(true);
@@ -120,7 +185,9 @@
 const single = ref(true);
 const multiple = ref(true);
 const total = ref(0);
-
+const deptName = ref('');
+const deptOptions = ref<DeptVO[]>([]);
+const deptTreeRef = ref<ElTreeInstance>();
 const postFormRef = ref<ElFormInstance>();
 const queryFormRef = ref<ElFormInstance>();
 
@@ -131,12 +198,14 @@
 
 const initFormData: PostForm = {
   postId: undefined,
+  deptId: undefined,
   postCode: '',
   postName: '',
+  postCategory: '',
   postSort: 0,
-  status: "0",
+  status: '0',
   remark: ''
-}
+};
 
 const data = reactive<PageData<PostForm, PostQuery>>({
   form: { ...initFormData },
@@ -145,16 +214,47 @@
     pageSize: 10,
     postCode: '',
     postName: '',
+    postCategory: '',
     status: ''
   },
   rules: {
-    postName: [{ required: true, message: "宀椾綅鍚嶇О涓嶈兘涓虹┖", trigger: "blur" }],
-    postCode: [{ required: true, message: "宀椾綅缂栫爜涓嶈兘涓虹┖", trigger: "blur" }],
-    postSort: [{ required: true, message: "宀椾綅椤哄簭涓嶈兘涓虹┖", trigger: "blur" }],
+    postName: [{ required: true, message: '宀椾綅鍚嶇О涓嶈兘涓虹┖', trigger: 'blur' }],
+    postCode: [{ required: true, message: '宀椾綅缂栫爜涓嶈兘涓虹┖', trigger: 'blur' }],
+    deptId: [{ required: true, message: '閮ㄩ棬涓嶈兘涓虹┖', trigger: 'blur' }],
+    postSort: [{ required: true, message: '宀椾綅椤哄簭涓嶈兘涓虹┖', trigger: 'blur' }]
   }
 });
 
 const { queryParams, form, rules } = toRefs<PageData<PostForm, PostQuery>>(data);
+
+/** 閫氳繃鏉′欢杩囨护鑺傜偣  */
+const filterNode = (value: string, data: any) => {
+  if (!value) return true;
+  return data.label.indexOf(value) !== -1;
+};
+
+/** 鏍规嵁鍚嶇О绛涢�夐儴闂ㄦ爲 */
+watchEffect(
+  () => {
+    deptTreeRef.value?.filter(deptName.value);
+  },
+  {
+    flush: 'post' // watchEffect浼氬湪DOM鎸傝浇鎴栬�呮洿鏂颁箣鍓嶅氨浼氳Е鍙戯紝姝ゅ睘鎬ф帶鍒跺湪DOM鍏冪礌鏇存柊鍚庤繍琛�
+  }
+);
+
+/** 鏌ヨ閮ㄩ棬涓嬫媺鏍戠粨鏋� */
+const getTreeSelect = async () => {
+  const res = await api.deptTreeSelect();
+  deptOptions.value = res.data;
+};
+
+/** 鑺傜偣鍗曞嚮浜嬩欢 */
+const handleNodeClick = (data: DeptVO) => {
+  queryParams.value.belongDeptId = data.id;
+  queryParams.value.deptId = undefined;
+  handleQuery();
+};
 
 /** 鏌ヨ宀椾綅鍒楄〃 */
 const getList = async () => {
@@ -163,39 +263,54 @@
   postList.value = res.rows;
   total.value = res.total;
   loading.value = false;
-}
+};
+
 /** 鍙栨秷鎸夐挳 */
 const cancel = () => {
   reset();
   dialog.visible = false;
-}
+};
+
 /** 琛ㄥ崟閲嶇疆 */
 const reset = () => {
   form.value = { ...initFormData };
   postFormRef.value?.resetFields();
-}
+};
+
 /** 鎼滅储鎸夐挳鎿嶄綔 */
 const handleQuery = () => {
   queryParams.value.pageNum = 1;
+  if (queryParams.value.deptId) {
+    queryParams.value.belongDeptId = undefined;
+  }
   getList();
-}
+};
+
 /** 閲嶇疆鎸夐挳鎿嶄綔 */
 const resetQuery = () => {
   queryFormRef.value?.resetFields();
+  queryParams.value.pageNum = 1;
+  queryParams.value.deptId = undefined;
+  deptTreeRef.value?.setCurrentKey(undefined);
+  /** 娓呯┖宸﹁竟閮ㄩ棬鏍戦�変腑鍊� */
+  queryParams.value.belongDeptId = undefined;
   handleQuery();
-}
+};
+
 /** 澶氶�夋閫変腑鏁版嵁 */
 const handleSelectionChange = (selection: PostVO[]) => {
-  ids.value = selection.map(item => item.postId);
+  ids.value = selection.map((item) => item.postId);
   single.value = selection.length != 1;
   multiple.value = !selection.length;
-}
+};
+
 /** 鏂板鎸夐挳鎿嶄綔 */
 const handleAdd = () => {
   reset();
   dialog.visible = true;
-  dialog.title = "娣诲姞宀椾綅";
-}
+  dialog.title = '娣诲姞宀椾綅';
+};
+
 /** 淇敼鎸夐挳鎿嶄綔 */
 const handleUpdate = async (row?: PostVO) => {
   reset();
@@ -203,35 +318,43 @@
   const res = await getPost(postId);
   Object.assign(form.value, res.data);
   dialog.visible = true;
-  dialog.title = "淇敼宀椾綅";
-}
+  dialog.title = '淇敼宀椾綅';
+};
+
 /** 鎻愪氦鎸夐挳 */
 const submitForm = () => {
   postFormRef.value?.validate(async (valid: boolean) => {
     if (valid) {
       form.value.postId ? await updatePost(form.value) : await addPost(form.value);
-      proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+      proxy?.$modal.msgSuccess('鎿嶄綔鎴愬姛');
       dialog.visible = false;
       await getList();
     }
   });
-}
+};
+
 /** 鍒犻櫎鎸夐挳鎿嶄綔 */
 const handleDelete = async (row?: PostVO) => {
   const postIds = row?.postId || ids.value;
   await proxy?.$modal.confirm('鏄惁纭鍒犻櫎宀椾綅缂栧彿涓�"' + postIds + '"鐨勬暟鎹」锛�');
   await delPost(postIds);
   await getList();
-  proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-}
+  proxy?.$modal.msgSuccess('鍒犻櫎鎴愬姛');
+};
+
 /** 瀵煎嚭鎸夐挳鎿嶄綔 */
 const handleExport = () => {
-  proxy?.download("system/post/export", {
-    ...queryParams.value
-  }, `post_${new Date().getTime()}.xlsx`);
-}
+  proxy?.download(
+    'system/post/export',
+    {
+      ...queryParams.value
+    },
+    `post_${new Date().getTime()}.xlsx`
+  );
+};
 
 onMounted(() => {
+  getTreeSelect(); // 鍒濆鍖栭儴闂ㄦ暟鎹�
   getList();
 });
 </script>
diff --git a/src/views/system/role/authUser.vue b/src/views/system/role/authUser.vue
index 7ad8a5a..6394804 100644
--- a/src/views/system/role/authUser.vue
+++ b/src/views/system/role/authUser.vue
@@ -1,13 +1,13 @@
 <template>
   <div class="p-2">
     <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
-      <div class="search" v-show="showSearch">
-        <el-form :model="queryParams" ref="queryFormRef" :inline="true">
+      <div v-show="showSearch" class="search">
+        <el-form ref="queryFormRef" :model="queryParams" :inline="true">
           <el-form-item label="鐢ㄦ埛鍚嶇О" prop="userName">
-            <el-input v-model="queryParams.userName" placeholder="璇疯緭鍏ョ敤鎴峰悕绉�" clearable style="width: 240px" @keyup.enter="handleQuery" />
+            <el-input v-model="queryParams.userName" placeholder="璇疯緭鍏ョ敤鎴峰悕绉�" clearable @keyup.enter="handleQuery" />
           </el-form-item>
           <el-form-item label="鎵嬫満鍙风爜" prop="phonenumber">
-            <el-input v-model="queryParams.phonenumber" placeholder="璇疯緭鍏ユ墜鏈哄彿鐮�" clearable style="width: 240px" @keyup.enter="handleQuery" />
+            <el-input v-model="queryParams.phonenumber" placeholder="璇疯緭鍏ユ墜鏈哄彿鐮�" clearable @keyup.enter="handleQuery" />
           </el-form-item>
           <el-form-item>
             <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
@@ -20,17 +20,17 @@
       <template #header>
         <el-row :gutter="10">
           <el-col :span="1.5">
-            <el-button type="primary" plain icon="Plus" @click="openSelectUser" v-hasPermi="['system:role:add']">娣诲姞鐢ㄦ埛</el-button>
+            <el-button v-hasPermi="['system:role:add']" type="primary" plain icon="Plus" @click="openSelectUser">娣诲姞鐢ㄦ埛</el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="danger" plain icon="CircleClose" :disabled="multiple" @click="cancelAuthUserAll" v-hasPermi="['system:role:remove']">
+            <el-button v-hasPermi="['system:role:remove']" type="danger" plain icon="CircleClose" :disabled="multiple" @click="cancelAuthUserAll">
               鎵归噺鍙栨秷鎺堟潈
             </el-button>
           </el-col>
           <el-col :span="1.5">
             <el-button type="warning" plain icon="Close" @click="handleClose">鍏抽棴</el-button>
           </el-col>
-          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList" :search="true"></right-toolbar>
+          <right-toolbar v-model:showSearch="showSearch" :search="true" @query-table="getList"></right-toolbar>
         </el-row>
       </template>
       <el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
@@ -52,28 +52,28 @@
         <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="CircleClose" @click="cancelAuthUser(scope.row)" v-hasPermi="['system:role:remove']"> </el-button>
+              <el-button v-hasPermi="['system:role:remove']" link type="primary" icon="CircleClose" @click="cancelAuthUser(scope.row)"> </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" />
-      <select-user ref="selectRef" :roleId="queryParams.roleId" @ok="handleQuery" />
+      <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
+      <select-user ref="selectRef" :role-id="queryParams.roleId" @ok="handleQuery" />
     </el-card>
   </div>
 </template>
 
 <script setup name="AuthUser" lang="ts">
-import { allocatedUserList, authUserCancel, authUserCancelAll } from "@/api/system/role";
-import { UserQuery } from "@/api/system/user/types";
-import { UserVO } from "@/api/system/user/types";
-import SelectUser from "./selectUser.vue";
-
+import { allocatedUserList, authUserCancel, authUserCancelAll } from '@/api/system/role';
+import { UserQuery } from '@/api/system/user/types';
+import { UserVO } from '@/api/system/user/types';
+import SelectUser from './selectUser.vue';
+import { RouteLocationNormalized } from 'vue-router';
 
 const route = useRoute();
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
-const { sys_normal_disable } = toRefs<any>(proxy?.useDict("sys_normal_disable"));
+const { sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_normal_disable'));
 
 const userList = ref<UserVO[]>([]);
 const loading = ref(true);
@@ -90,7 +90,7 @@
   pageSize: 10,
   roleId: route.params.roleId as string,
   userName: undefined,
-  phonenumber: undefined,
+  phonenumber: undefined
 });
 
 /** 鏌ヨ鎺堟潈鐢ㄦ埛鍒楄〃 */
@@ -100,47 +100,57 @@
   userList.value = res.rows;
   total.value = res.total;
   loading.value = false;
-}
+};
 // 杩斿洖鎸夐挳
 const handleClose = () => {
-  const obj = { path: "/system/role" };
+  const obj: RouteLocationNormalized = {
+    path: '/system/role',
+    fullPath: '',
+    hash: '',
+    matched: [],
+    meta: undefined,
+    name: undefined,
+    params: undefined,
+    query: undefined,
+    redirectedFrom: undefined
+  };
   proxy?.$tab.closeOpenPage(obj);
-}
+};
 /** 鎼滅储鎸夐挳鎿嶄綔 */
 const handleQuery = () => {
   queryParams.pageNum = 1;
   getList();
-}
+};
 /** 閲嶇疆鎸夐挳鎿嶄綔 */
 const resetQuery = () => {
   queryFormRef.value?.resetFields();
   handleQuery();
-}
+};
 // 澶氶�夋閫変腑鏁版嵁
 const handleSelectionChange = (selection: UserVO[]) => {
-  userIds.value = selection.map(item => item.userId);
+  userIds.value = selection.map((item) => item.userId);
   multiple.value = !selection.length;
-}
+};
 /** 鎵撳紑鎺堟潈鐢ㄦ埛琛ㄥ脊绐� */
 const openSelectUser = () => {
   selectRef.value?.show();
-}
+};
 /** 鍙栨秷鎺堟潈鎸夐挳鎿嶄綔 */
 const cancelAuthUser = async (row: UserVO) => {
   await proxy?.$modal.confirm('纭瑕佸彇娑堣鐢ㄦ埛"' + row.userName + '"瑙掕壊鍚楋紵');
   await authUserCancel({ userId: row.userId, roleId: queryParams.roleId });
   await getList();
-  proxy?.$modal.msgSuccess("鍙栨秷鎺堟潈鎴愬姛");
-}
+  proxy?.$modal.msgSuccess('鍙栨秷鎺堟潈鎴愬姛');
+};
 /** 鎵归噺鍙栨秷鎺堟潈鎸夐挳鎿嶄綔 */
 const cancelAuthUserAll = async () => {
   const roleId = queryParams.roleId;
-  const uIds = userIds.value.join(",");
-  await proxy?.$modal.confirm("鏄惁鍙栨秷閫変腑鐢ㄦ埛鎺堟潈鏁版嵁椤�?");
+  const uIds = userIds.value.join(',');
+  await proxy?.$modal.confirm('鏄惁鍙栨秷閫変腑鐢ㄦ埛鎺堟潈鏁版嵁椤�?');
   await authUserCancelAll({ roleId: roleId, userIds: uIds });
   await getList();
-  proxy?.$modal.msgSuccess("鍙栨秷鎺堟潈鎴愬姛");
-}
+  proxy?.$modal.msgSuccess('鍙栨秷鎺堟潈鎴愬姛');
+};
 
 onMounted(() => {
   getList();
diff --git a/src/views/system/role/index.vue b/src/views/system/role/index.vue
index bb6fc2c..bd84af3 100644
--- a/src/views/system/role/index.vue
+++ b/src/views/system/role/index.vue
@@ -1,17 +1,17 @@
 <template>
   <div class="p-2">
     <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
-      <div class="mb-[10px]" v-show="showSearch">
+      <div v-show="showSearch" class="mb-[10px]">
         <el-card shadow="hover">
-          <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
+          <el-form ref="queryFormRef" :model="queryParams" :inline="true">
             <el-form-item label="瑙掕壊鍚嶇О" prop="roleName">
-              <el-input v-model="queryParams.roleName" placeholder="璇疯緭鍏ヨ鑹插悕绉�" clearable style="width: 240px" @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.roleName" placeholder="璇疯緭鍏ヨ鑹插悕绉�" clearable @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item label="鏉冮檺瀛楃" prop="roleKey">
-              <el-input v-model="queryParams.roleKey" placeholder="璇疯緭鍏ユ潈闄愬瓧绗�" clearable style="width: 240px" @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.roleKey" placeholder="璇疯緭鍏ユ潈闄愬瓧绗�" clearable @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item label="鐘舵��" prop="status">
-              <el-select v-model="queryParams.status" placeholder="瑙掕壊鐘舵��" clearable style="width: 240px">
+              <el-select v-model="queryParams.status" placeholder="瑙掕壊鐘舵��" clearable >
                 <el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
               </el-select>
             </el-form-item>
@@ -28,8 +28,8 @@
             </el-form-item>
 
             <el-form-item>
-              <el-button type="primary" @click="handleQuery" icon="Search">鎼滅储</el-button>
-              <el-button @click="resetQuery" icon="Refresh">閲嶇疆</el-button>
+              <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
+              <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
             </el-form-item>
           </el-form>
         </el-card>
@@ -40,24 +40,24 @@
       <template #header>
         <el-row :gutter="10">
           <el-col :span="1.5">
-            <el-button type="primary" plain @click="handleAdd()" icon="Plus" v-hasPermi="['system:role:add']">鏂板</el-button>
+            <el-button v-hasPermi="['system:role:add']" type="primary" plain icon="Plus" @click="handleAdd()">鏂板</el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="success" plain @click="handleUpdate()" :disabled="single" icon="Edit" v-hasPermi="['system:role:edit']">淇敼</el-button>
+            <el-button v-hasPermi="['system:role:edit']" type="success" plain :disabled="single" icon="Edit" @click="handleUpdate()">淇敼</el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="danger" plain :disabled="ids.length === 0" @click="handleDelete()" v-hasPermi="['system:role:delete']">鍒犻櫎</el-button>
+            <el-button v-hasPermi="['system:role:delete']" type="danger" plain :disabled="ids.length === 0" @click="handleDelete()">鍒犻櫎</el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:role:export']">瀵煎嚭</el-button>
+            <el-button v-hasPermi="['system:role:export']" type="warning" plain icon="Download" @click="handleExport">瀵煎嚭</el-button>
           </el-col>
-          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+          <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
         </el-row>
       </template>
 
       <el-table ref="roleTableRef" v-loading="loading" :data="roleList" @selection-change="handleSelectionChange">
         <el-table-column type="selection" width="55" align="center" />
-        <el-table-column label="瑙掕壊缂栧彿" prop="roleId" width="120" v-if="false" />
+        <el-table-column v-if="false" label="瑙掕壊缂栧彿" prop="roleId" width="120" />
         <el-table-column label="瑙掕壊鍚嶇О" prop="roleName" :show-overflow-tooltip="true" width="150" />
         <el-table-column label="鏉冮檺瀛楃" prop="roleKey" :show-overflow-tooltip="true" width="200" />
         <el-table-column label="鏄剧ず椤哄簭" prop="roleSort" width="100" />
@@ -74,17 +74,17 @@
 
         <el-table-column fixed="right" label="鎿嶄綔" width="180">
           <template #default="scope">
-            <el-tooltip content="淇敼" placement="top" v-if="scope.row.roleId !== 1">
-              <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:role:edit']"></el-button>
+            <el-tooltip v-if="scope.row.roleId !== 1" content="淇敼" placement="top">
+              <el-button v-hasPermi="['system:role:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
             </el-tooltip>
-            <el-tooltip content="鍒犻櫎" placement="top" v-if="scope.row.roleId !== 1">
-              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:role:remove']"></el-button>
+            <el-tooltip v-if="scope.row.roleId !== 1" content="鍒犻櫎" placement="top">
+              <el-button v-hasPermi="['system:role:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
             </el-tooltip>
-            <el-tooltip content="鏁版嵁鏉冮檺" placement="top" v-if="scope.row.roleId !== 1">
-              <el-button link type="primary" icon="CircleCheck" @click="handleDataScope(scope.row)" v-hasPermi="['system:role:edit']"></el-button>
+            <el-tooltip v-if="scope.row.roleId !== 1" content="鏁版嵁鏉冮檺" placement="top">
+              <el-button v-hasPermi="['system:role:edit']" link type="primary" icon="CircleCheck" @click="handleDataScope(scope.row)"></el-button>
             </el-tooltip>
-            <el-tooltip content="鍒嗛厤鐢ㄦ埛" placement="top" v-if="scope.row.roleId !== 1">
-              <el-button link type="primary" icon="User" @click="handleAuthUser(scope.row)" v-hasPermi="['system:role:edit']"></el-button>
+            <el-tooltip v-if="scope.row.roleId !== 1" content="鍒嗛厤鐢ㄦ埛" placement="top">
+              <el-button v-hasPermi="['system:role:edit']" link type="primary" icon="User" @click="handleAuthUser(scope.row)"></el-button>
             </el-tooltip>
           </template>
         </el-table-column>
@@ -99,7 +99,7 @@
       />
     </el-card>
 
-    <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+    <el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
       <el-form ref="roleFormRef" :model="form" :rules="rules" label-width="100px">
         <el-form-item label="瑙掕壊鍚嶇О" prop="roleName">
           <el-input v-model="form.roleName" placeholder="璇疯緭鍏ヨ鑹插悕绉�" />
@@ -120,9 +120,7 @@
         </el-form-item>
         <el-form-item label="鐘舵��">
           <el-radio-group v-model="form.status">
-            <el-radio v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.value">{{
-              dict.label
-            }}</el-radio>
+            <el-radio v-for="dict in sys_normal_disable" :key="dict.value" :value="dict.value">{{ dict.label }}</el-radio>
           </el-radio-group>
         </el-form-item>
         <el-form-item label="鑿滃崟鏉冮檺">
@@ -130,10 +128,10 @@
           <el-checkbox v-model="menuNodeAll" @change="handleCheckedTreeNodeAll($event, 'menu')">鍏ㄩ��/鍏ㄤ笉閫�</el-checkbox>
           <el-checkbox v-model="form.menuCheckStrictly" @change="handleCheckedTreeConnect($event, 'menu')">鐖跺瓙鑱斿姩</el-checkbox>
           <el-tree
+            ref="menuRef"
             class="tree-border"
             :data="menuOptions"
             show-checkbox
-            ref="menuRef"
             node-key="id"
             :check-strictly="!form.menuCheckStrictly"
             empty-text="鍔犺浇涓紝璇风◢鍊�"
@@ -153,8 +151,8 @@
     </el-dialog>
 
     <!-- 鍒嗛厤瑙掕壊鏁版嵁鏉冮檺瀵硅瘽妗� -->
-    <el-dialog :title="dialog.title" v-model="openDataScope" width="500px" append-to-body>
-      <el-form :model="form" label-width="80px" ref="dataScopeRef">
+    <el-dialog v-model="openDataScope" :title="dialog.title" width="500px" append-to-body>
+      <el-form ref="dataScopeRef" :model="form" label-width="80px">
         <el-form-item label="瑙掕壊鍚嶇О">
           <el-input v-model="form.roleName" :disabled="true" />
         </el-form-item>
@@ -166,16 +164,16 @@
             <el-option v-for="item in dataScopeOptions" :key="item.value" :label="item.label" :value="item.value"></el-option>
           </el-select>
         </el-form-item>
-        <el-form-item label="鏁版嵁鏉冮檺" v-show="form.dataScope === '2'">
+        <el-form-item v-show="form.dataScope === '2'" label="鏁版嵁鏉冮檺">
           <el-checkbox v-model="deptExpand" @change="handleCheckedTreeExpand($event, 'dept')">灞曞紑/鎶樺彔</el-checkbox>
           <el-checkbox v-model="deptNodeAll" @change="handleCheckedTreeNodeAll($event, 'dept')">鍏ㄩ��/鍏ㄤ笉閫�</el-checkbox>
           <el-checkbox v-model="form.deptCheckStrictly" @change="handleCheckedTreeConnect($event, 'dept')">鐖跺瓙鑱斿姩</el-checkbox>
           <el-tree
+            ref="deptRef"
             class="tree-border"
             :data="deptOptions"
             show-checkbox
             default-expand-all
-            ref="deptRef"
             node-key="id"
             :check-strictly="!form.deptCheckStrictly"
             empty-text="鍔犺浇涓紝璇风◢鍊�"
@@ -194,7 +192,7 @@
 </template>
 
 <script setup name="Role" lang="ts">
-import { addRole, changeRoleStatus, dataScope, delRole, getRole, listRole, updateRole, deptTreeSelect } from "@/api/system/role";
+import { addRole, changeRoleStatus, dataScope, delRole, getRole, listRole, updateRole, deptTreeSelect } from '@/api/system/role';
 import { roleMenuTreeselect, treeselect as menuTreeselect } from '@/api/system/menu/index';
 import { RoleVO, RoleForm, RoleQuery, DeptTreeOption } from '@/api/system/role/types';
 import { MenuTreeOption, RoleMenuTree } from '@/api/system/menu/types';
@@ -204,29 +202,29 @@
 const { sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_normal_disable'));
 
 const roleList = ref<RoleVO[]>();
-const loading = ref(true)
-const showSearch = ref(true)
-const ids = ref<Array<string | number>>([])
-const single = ref(true)
-const multiple = ref(true)
-const total = ref(0)
-const dateRange = ref<[DateModelType, DateModelType]>(['', ''])
-const menuOptions = ref<MenuTreeOption[]>([])
-const menuExpand = ref(false)
-const menuNodeAll = ref(false)
-const deptExpand = ref(true)
-const deptNodeAll = ref(false)
-const deptOptions = ref<DeptTreeOption[]>([])
-const openDataScope = ref(false)
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
+const menuOptions = ref<MenuTreeOption[]>([]);
+const menuExpand = ref(false);
+const menuNodeAll = ref(false);
+const deptExpand = ref(true);
+const deptNodeAll = ref(false);
+const deptOptions = ref<DeptTreeOption[]>([]);
+const openDataScope = ref(false);
 
 /** 鏁版嵁鑼冨洿閫夐」*/
 const dataScopeOptions = ref([
-  { value: "1", label: "鍏ㄩ儴鏁版嵁鏉冮檺" },
-  { value: "2", label: "鑷畾鏁版嵁鏉冮檺" },
-  { value: "3", label: "鏈儴闂ㄦ暟鎹潈闄�" },
-  { value: "4", label: "鏈儴闂ㄥ強浠ヤ笅鏁版嵁鏉冮檺" },
-  { value: "5", label: "浠呮湰浜烘暟鎹潈闄�" }
-])
+  { value: '1', label: '鍏ㄩ儴鏁版嵁鏉冮檺' },
+  { value: '2', label: '鑷畾鏁版嵁鏉冮檺' },
+  { value: '3', label: '鏈儴闂ㄦ暟鎹潈闄�' },
+  { value: '4', label: '鏈儴闂ㄥ強浠ヤ笅鏁版嵁鏉冮檺' },
+  { value: '5', label: '浠呮湰浜烘暟鎹潈闄�' }
+]);
 
 const queryFormRef = ref<ElFormInstance>();
 const roleFormRef = ref<ElFormInstance>();
@@ -245,8 +243,8 @@
   remark: '',
   dataScope: '1',
   menuIds: [],
-  deptIds: [],
-}
+  deptIds: []
+};
 
 const data = reactive<PageData<RoleForm, RoleQuery>>({
   form: { ...initForm },
@@ -255,15 +253,15 @@
     pageSize: 10,
     roleName: '',
     roleKey: '',
-    status: '',
+    status: ''
   },
   rules: {
-    roleName: [{ required: true, message: "瑙掕壊鍚嶇О涓嶈兘涓虹┖", trigger: "blur" }],
-    roleKey: [{ required: true, message: "鏉冮檺瀛楃涓嶈兘涓虹┖", trigger: "blur" }],
-    roleSort: [{ required: true, message: "瑙掕壊椤哄簭涓嶈兘涓虹┖", trigger: "blur" }]
+    roleName: [{ required: true, message: '瑙掕壊鍚嶇О涓嶈兘涓虹┖', trigger: 'blur' }],
+    roleKey: [{ required: true, message: '鏉冮檺瀛楃涓嶈兘涓虹┖', trigger: 'blur' }],
+    roleSort: [{ required: true, message: '瑙掕壊椤哄簭涓嶈兘涓虹┖', trigger: 'blur' }]
   }
-})
-const { form, queryParams, rules } = toRefs(data)
+});
+const { form, queryParams, rules } = toRefs(data);
 
 const dialog = reactive<DialogOption>({
   visible: false,
@@ -274,13 +272,13 @@
  * 鏌ヨ瑙掕壊鍒楄〃
  */
 const getList = () => {
-  loading.value = true
-  listRole(proxy?.addDateRange(queryParams.value, dateRange.value)).then(res => {
-    roleList.value = res.rows
-    total.value = res.total
-    loading.value = false
-  })
-}
+  loading.value = true;
+  listRole(proxy?.addDateRange(queryParams.value, dateRange.value)).then((res) => {
+    roleList.value = res.rows;
+    total.value = res.total;
+    loading.value = false;
+  });
+};
 
 /**
  * 鎼滅储鎸夐挳鎿嶄綔
@@ -288,14 +286,14 @@
 const handleQuery = () => {
   queryParams.value.pageNum = 1;
   getList();
-}
+};
 
 /** 閲嶇疆 */
 const resetQuery = () => {
-  dateRange.value = ['', '']
+  dateRange.value = ['', ''];
   queryFormRef.value?.resetFields();
   handleQuery();
-}
+};
 /**鍒犻櫎鎸夐挳鎿嶄綔 */
 const handleDelete = async (row?: RoleVO) => {
   const roleids = row?.roleId || ids.value;
@@ -303,43 +301,47 @@
   await delRole(roleids);
   getList();
   proxy?.$modal.msgSuccess('鍒犻櫎鎴愬姛');
-}
+};
 
 /** 瀵煎嚭鎸夐挳鎿嶄綔 */
 const handleExport = () => {
-  proxy?.download("system/role/export", {
-    ...queryParams.value,
-  }, `role_${new Date().getTime()}.xlsx`)
-}
+  proxy?.download(
+    'system/role/export',
+    {
+      ...queryParams.value
+    },
+    `role_${new Date().getTime()}.xlsx`
+  );
+};
 /** 澶氶�夋閫変腑鏁版嵁 */
 const handleSelectionChange = (selection: RoleVO[]) => {
   ids.value = selection.map((item: RoleVO) => item.roleId);
   single.value = selection.length != 1;
   multiple.value = !selection.length;
-}
+};
 
 /** 瑙掕壊鐘舵�佷慨鏀� */
 const handleStatusChange = async (row: RoleVO) => {
-  let text = row.status === "0" ? "鍚敤" : "鍋滅敤";
+  let text = row.status === '0' ? '鍚敤' : '鍋滅敤';
   try {
     await proxy?.$modal.confirm('纭瑕�"' + text + '""' + row.roleName + '"瑙掕壊鍚�?');
     await changeRoleStatus(row.roleId, row.status);
-    proxy?.$modal.msgSuccess(text + "鎴愬姛");
+    proxy?.$modal.msgSuccess(text + '鎴愬姛');
   } catch {
-    row.status = row.status === "0" ? "1" : "0";
+    row.status = row.status === '0' ? '1' : '0';
   }
-}
+};
 
 /** 鍒嗛厤鐢ㄦ埛 */
 const handleAuthUser = (row: RoleVO) => {
-  router.push("/system/role-auth/user/" + row.roleId);
-}
+  router.push('/system/role-auth/user/' + row.roleId);
+};
 
 /** 鏌ヨ鑿滃崟鏍戠粨鏋� */
 const getMenuTreeselect = async () => {
   const res = await menuTreeselect();
   menuOptions.value = res.data;
-}
+};
 /** 鎵�鏈夐儴闂ㄨ妭鐐规暟鎹� */
 const getDeptAllCheckedKeys = (): any => {
   // 鐩墠琚�変腑鐨勯儴闂ㄨ妭鐐�
@@ -349,67 +351,65 @@
   if (halfCheckedKeys) {
     checkedKeys?.unshift.apply(checkedKeys, halfCheckedKeys);
   }
-  return checkedKeys
-}
+  return checkedKeys;
+};
 /** 閲嶇疆鏂板鐨勮〃鍗曚互鍙婂叾浠栨暟鎹�  */
 const reset = () => {
   menuRef.value?.setCheckedKeys([]);
-  menuExpand.value = false
-  menuNodeAll.value = false
-  deptExpand.value = true
-  deptNodeAll.value = false
+  menuExpand.value = false;
+  menuNodeAll.value = false;
+  deptExpand.value = true;
+  deptNodeAll.value = false;
   form.value = { ...initForm };
   roleFormRef.value?.resetFields();
-
-}
+};
 
 /** 娣诲姞瑙掕壊 */
 const handleAdd = () => {
   reset();
   getMenuTreeselect();
   dialog.visible = true;
-  dialog.title = "娣诲姞瑙掕壊";
-}
+  dialog.title = '娣诲姞瑙掕壊';
+};
 /** 淇敼瑙掕壊 */
 const handleUpdate = async (row?: RoleVO) => {
   reset();
-  const roleId = row?.roleId || ids.value[0]
+  const roleId = row?.roleId || ids.value[0];
   const { data } = await getRole(roleId);
   Object.assign(form.value, data);
   form.value.roleSort = Number(form.value.roleSort);
   const res = await getRoleMenuTreeselect(roleId);
-  dialog.title = "淇敼瑙掕壊";
+  dialog.title = '淇敼瑙掕壊';
   dialog.visible = true;
   res.checkedKeys.forEach((v) => {
     nextTick(() => {
       menuRef.value?.setChecked(v, true, false);
-    })
-  })
-
-}
+    });
+  });
+};
 /** 鏍规嵁瑙掕壊ID鏌ヨ鑿滃崟鏍戠粨鏋� */
 const getRoleMenuTreeselect = (roleId: string | number) => {
   return roleMenuTreeselect(roleId).then((res): RoleMenuTree => {
     menuOptions.value = res.data.menus;
     return res.data;
-  })
-}
+  });
+};
 /** 鏍规嵁瑙掕壊ID鏌ヨ閮ㄩ棬鏍戠粨鏋� */
 const getRoleDeptTreeSelect = async (roleId: string | number) => {
   const res = await deptTreeSelect(roleId);
   deptOptions.value = res.data.depts;
   return res.data;
-}
+};
 /** 鏍戞潈闄愶紙灞曞紑/鎶樺彔锛�*/
 const handleCheckedTreeExpand = (value: boolean, type: string) => {
-  if (type == "menu") {
+  if (type == 'menu') {
     let treeList = menuOptions.value;
     for (let i = 0; i < treeList.length; i++) {
       if (menuRef.value) {
         menuRef.value.store.nodesMap[treeList[i].id].expanded = value;
       }
     }
-  } else if (type == "dept") {
+  } else if (type == 'dept') {
     let treeList = deptOptions.value;
     for (let i = 0; i < treeList.length; i++) {
       if (deptRef.value) {
@@ -417,23 +417,23 @@
       }
     }
   }
-}
+};
 /** 鏍戞潈闄愶紙鍏ㄩ��/鍏ㄤ笉閫夛級 */
 const handleCheckedTreeNodeAll = (value: any, type: string) => {
-  if (type == "menu") {
-    menuRef.value?.setCheckedNodes(value ? menuOptions.value as any : []);
-  } else if (type == "dept") {
-    deptRef.value?.setCheckedNodes(value ? deptOptions.value as any : []);
+  if (type == 'menu') {
+    menuRef.value?.setCheckedNodes(value ? (menuOptions.value as any) : []);
+  } else if (type == 'dept') {
+    deptRef.value?.setCheckedNodes(value ? (deptOptions.value as any) : []);
   }
-}
+};
 /** 鏍戞潈闄愶紙鐖跺瓙鑱斿姩锛� */
 const handleCheckedTreeConnect = (value: any, type: string) => {
-  if (type == "menu") {
+  if (type == 'menu') {
     form.value.menuCheckStrictly = value;
-  } else if (type == "dept") {
+  } else if (type == 'dept') {
     form.value.deptCheckStrictly = value;
   }
-}
+};
 /** 鎵�鏈夎彍鍗曡妭鐐规暟鎹� */
 const getMenuAllCheckedKeys = (): any => {
   // 鐩墠琚�変腑鐨勮彍鍗曡妭鐐�
@@ -444,57 +444,57 @@
     checkedKeys?.unshift.apply(checkedKeys, halfCheckedKeys);
   }
   return checkedKeys;
-}
+};
 /** 鎻愪氦鎸夐挳 */
 const submitForm = () => {
   roleFormRef.value?.validate(async (valid: boolean) => {
     if (valid) {
-      form.value.menuIds = getMenuAllCheckedKeys()
+      form.value.menuIds = getMenuAllCheckedKeys();
       form.value.roleId ? await updateRole(form.value) : await addRole(form.value);
-      proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛")
-      dialog.visible = false
-      getList()
+      proxy?.$modal.msgSuccess('鎿嶄綔鎴愬姛');
+      dialog.visible = false;
+      getList();
     }
-  })
-}
+  });
+};
 /** 鍙栨秷鎸夐挳 */
 const cancel = () => {
-  reset()
+  reset();
   dialog.visible = false;
-}
+};
 /** 閫夋嫨瑙掕壊鏉冮檺鑼冨洿瑙﹀彂 */
 const dataScopeSelectChange = (value: string) => {
-  if (value !== "2") {
-    deptRef.value?.setCheckedKeys([])
+  if (value !== '2') {
+    deptRef.value?.setCheckedKeys([]);
   }
-}
+};
 /** 鍒嗛厤鏁版嵁鏉冮檺鎿嶄綔 */
 const handleDataScope = async (row: RoleVO) => {
   const response = await getRole(row.roleId);
   Object.assign(form.value, response.data);
   const res = await getRoleDeptTreeSelect(row.roleId);
   openDataScope.value = true;
-  dialog.title = "鍒嗛厤鏁版嵁鏉冮檺";
+  dialog.title = '鍒嗛厤鏁版嵁鏉冮檺';
   await nextTick(() => {
     deptRef.value?.setCheckedKeys(res.checkedKeys);
-  })
-}
+  });
+};
 /** 鎻愪氦鎸夐挳锛堟暟鎹潈闄愶級 */
 const submitDataScope = async () => {
   if (form.value.roleId) {
     form.value.deptIds = getDeptAllCheckedKeys();
     await dataScope(form.value);
-    proxy?.$modal.msgSuccess("淇敼鎴愬姛");
+    proxy?.$modal.msgSuccess('淇敼鎴愬姛');
     openDataScope.value = false;
     getList();
   }
-}
+};
 /** 鍙栨秷鎸夐挳锛堟暟鎹潈闄愶級*/
 const cancelDataScope = () => {
   dataScopeRef.value?.resetFields();
   form.value = { ...initForm };
   openDataScope.value = false;
-}
+};
 
 onMounted(() => {
   getList();
diff --git a/src/views/system/role/selectUser.vue b/src/views/system/role/selectUser.vue
index e0cd433..ec7a257 100644
--- a/src/views/system/role/selectUser.vue
+++ b/src/views/system/role/selectUser.vue
@@ -1,7 +1,7 @@
 <template>
   <el-row>
-    <el-dialog title="閫夋嫨鐢ㄦ埛" v-model="visible" width="800px" top="5vh" append-to-body>
-      <el-form :model="queryParams" ref="queryFormRef" :inline="true">
+    <el-dialog v-model="visible" title="閫夋嫨鐢ㄦ埛" width="800px" top="5vh" append-to-body>
+      <el-form ref="queryFormRef" :model="queryParams" :inline="true">
         <el-form-item label="鐢ㄦ埛鍚嶇О" prop="userName">
           <el-input v-model="queryParams.userName" placeholder="璇疯緭鍏ョ敤鎴峰悕绉�" clearable @keyup.enter="handleQuery" />
         </el-form-item>
@@ -14,7 +14,7 @@
         </el-form-item>
       </el-form>
       <el-row>
-        <el-table @row-click="clickRow" ref="tableRef" :data="userList" @selection-change="handleSelectionChange" height="260px">
+        <el-table ref="tableRef" :data="userList" height="260px" @row-click="clickRow" @selection-change="handleSelectionChange">
           <el-table-column type="selection" width="55"></el-table-column>
           <el-table-column label="鐢ㄦ埛鍚嶇О" prop="userName" :show-overflow-tooltip="true" />
           <el-table-column label="鐢ㄦ埛鏄电О" prop="nickName" :show-overflow-tooltip="true" />
@@ -31,7 +31,7 @@
             </template>
           </el-table-column>
         </el-table>
-        <pagination v-if="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+        <pagination v-if="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
       </el-row>
       <template #footer>
         <div class="dialog-footer">
@@ -44,16 +44,16 @@
 </template>
 
 <script setup name="SelectUser" lang="ts">
-import { authUserSelectAll, unallocatedUserList } from "@/api/system/role";
+import { authUserSelectAll, unallocatedUserList } from '@/api/system/role';
 import { UserVO } from '@/api/system/user/types';
 import { UserQuery } from '@/api/system/user/types';
 
-
 const props = defineProps({
   roleId: {
-    type: [Number, String]
+    type: [Number, String],
+    required: true
   }
-})
+});
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 const { sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_normal_disable'));
@@ -69,7 +69,7 @@
   roleId: undefined,
   userName: undefined,
   phonenumber: undefined
-})
+});
 
 const tableRef = ref<ElTableInstance>();
 const queryFormRef = ref<ElFormInstance>();
@@ -78,7 +78,7 @@
   queryParams.roleId = props.roleId;
   getList();
   visible.value = true;
-}
+};
 
 /**
  * 閫夋嫨琛�
@@ -86,35 +86,35 @@
 const clickRow = (row: any) => {
   // ele鐨刡ug
   tableRef.value?.toggleRowSelection(row, false);
-}
+};
 /** 澶氶�夋閫変腑鏁版嵁 */
 const handleSelectionChange = (selection: UserVO[]) => {
   userIds.value = selection.map((item: UserVO) => item.userId);
-}
+};
 
 /** 鏌ヨ鏁版嵁 */
 const getList = async () => {
   const res = await unallocatedUserList(queryParams);
   userList.value = res.rows;
   total.value = res.total;
-}
+};
 /** 鎼滅储鎸夐挳鎿嶄綔 */
 const handleQuery = () => {
   queryParams.pageNum = 1;
   getList();
-}
+};
 /** 閲嶇疆鎸夐挳鎿嶄綔 */
 const resetQuery = () => {
   queryFormRef.value?.resetFields();
   getList();
-}
+};
 
-const emit = defineEmits(["ok"]);
+const emit = defineEmits(['ok']);
 /**閫夋嫨鎺堟潈鐢ㄦ埛鎿嶄綔 */
 const handleSelectUser = async () => {
   const roleId = queryParams.roleId;
   const ids = userIds.value.join(',');
-  if (ids == "") {
+  if (ids == '') {
     proxy?.$modal.msgError('璇烽�夋嫨瑕佸垎閰嶇殑鐢ㄦ埛');
     return;
   }
@@ -122,10 +122,10 @@
   proxy?.$modal.msgSuccess('鍒嗛厤鎴愬姛');
   emit('ok');
   visible.value = false;
-}
+};
 // 鏆撮湶
 defineExpose({
-  show,
+  show
 });
 </script>
 
diff --git a/src/views/system/tenant/index.vue b/src/views/system/tenant/index.vue
index 9a7fdf3..fabe18d 100644
--- a/src/views/system/tenant/index.vue
+++ b/src/views/system/tenant/index.vue
@@ -1,20 +1,20 @@
 <template>
   <div class="p-2">
     <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
-      <div class="mb-[10px]" v-show="showSearch">
+      <div v-show="showSearch" class="mb-[10px]">
         <el-card shadow="hover">
-          <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
+          <el-form ref="queryFormRef" :model="queryParams" :inline="true">
             <el-form-item label="绉熸埛缂栧彿" prop="tenantId">
-              <el-input v-model="queryParams.tenantId" placeholder="璇疯緭鍏ョ鎴风紪鍙�" clearable style="width: 240px" @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.tenantId" placeholder="璇疯緭鍏ョ鎴风紪鍙�" clearable @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item label="鑱旂郴浜�" prop="contactUserName">
-              <el-input v-model="queryParams.contactUserName" placeholder="璇疯緭鍏ヨ仈绯讳汉" clearable style="width: 240px" @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.contactUserName" placeholder="璇疯緭鍏ヨ仈绯讳汉" clearable @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item label="鑱旂郴鐢佃瘽" prop="contactPhone">
-              <el-input v-model="queryParams.contactPhone" placeholder="璇疯緭鍏ヨ仈绯荤數璇�" clearable style="width: 240px" @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.contactPhone" placeholder="璇疯緭鍏ヨ仈绯荤數璇�" clearable @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item label="浼佷笟鍚嶇О" prop="companyName">
-              <el-input v-model="queryParams.companyName" placeholder="璇疯緭鍏ヤ紒涓氬悕绉�" clearable style="width: 240px" @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.companyName" placeholder="璇疯緭鍏ヤ紒涓氬悕绉�" clearable @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item>
               <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
@@ -29,28 +29,28 @@
       <template #header>
         <el-row :gutter="10" class="mb8">
           <el-col :span="1.5">
-            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:tenant:add']">鏂板</el-button>
+            <el-button v-hasPermi="['system:tenant:add']" type="primary" plain icon="Plus" @click="handleAdd">鏂板</el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:tenant:edit']"
+            <el-button v-hasPermi="['system:tenant:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()"
               >淇敼</el-button
             >
           </el-col>
           <el-col :span="1.5">
-            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:tenant:remove']">
+            <el-button v-hasPermi="['system:tenant:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">
               鍒犻櫎
             </el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:tenant:export']">瀵煎嚭</el-button>
+            <el-button v-hasPermi="['system:tenant:export']" type="warning" plain icon="Download" @click="handleExport">瀵煎嚭</el-button>
           </el-col>
-          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+          <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
         </el-row>
       </template>
 
       <el-table v-loading="loading" :data="tenantList" @selection-change="handleSelectionChange">
         <el-table-column type="selection" width="55" align="center" />
-        <el-table-column label="id" align="center" prop="id" v-if="false" />
+        <el-table-column v-if="false" label="id" align="center" prop="id" />
         <el-table-column label="绉熸埛缂栧彿" align="center" prop="tenantId" />
         <el-table-column label="鑱旂郴浜�" align="center" prop="contactUserName" />
         <el-table-column label="鑱旂郴鐢佃瘽" align="center" prop="contactPhone" />
@@ -69,23 +69,23 @@
         <el-table-column width="150" label="鎿嶄綔" align="center" fixed="right" 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="['system:tenant:edit']"></el-button>
+              <el-button v-hasPermi="['system:tenant:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
             </el-tooltip>
             <el-tooltip content="鍚屾濂楅" placement="top">
-              <el-button link type="primary" icon="Refresh" @click="handleSyncTenantPackage(scope.row)" v-hasPermi="['system:tenant:edit']">
+              <el-button v-hasPermi="['system:tenant:edit']" link type="primary" icon="Refresh" @click="handleSyncTenantPackage(scope.row)">
               </el-button>
             </el-tooltip>
             <el-tooltip content="鍒犻櫎" placement="top">
-              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:tenant:remove']"></el-button>
+              <el-button v-hasPermi="['system:tenant:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></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" />
+      <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
     </el-card>
     <!-- 娣诲姞鎴栦慨鏀圭鎴峰璇濇 -->
-    <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+    <el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
       <el-form ref="tenantFormRef" :model="form" :rules="rules" label-width="80px">
         <el-form-item label="浼佷笟鍚嶇О" prop="companyName">
           <el-input v-model="form.companyName" placeholder="璇疯緭鍏ヤ紒涓氬悕绉�" />
@@ -100,7 +100,7 @@
           <el-input v-model="form.username" placeholder="璇疯緭鍏ョ郴缁熺敤鎴峰悕" maxlength="30" />
         </el-form-item>
         <el-form-item v-if="!form.id" label="鐢ㄦ埛瀵嗙爜" prop="password">
-          <el-input type="password" v-model="form.password" placeholder="璇疯緭鍏ョ郴缁熺敤鎴峰瘑鐮�" maxlength="20" />
+          <el-input v-model="form.password" type="password" placeholder="璇疯緭鍏ョ郴缁熺敤鎴峰瘑鐮�" maxlength="20" />
         </el-form-item>
         <el-form-item label="绉熸埛濂楅" prop="packageId">
           <el-select v-model="form.packageId" :disabled="!!form.tenantId" placeholder="璇烽�夋嫨绉熸埛濂楅" clearable style="width: 100%">
@@ -108,7 +108,7 @@
           </el-select>
         </el-form-item>
         <el-form-item label="杩囨湡鏃堕棿" prop="expireTime">
-          <el-date-picker clearable v-model="form.expireTime" type="datetime" value-format="YYYY-MM-DD HH:mm:ss" placeholder="璇烽�夋嫨杩囨湡鏃堕棿">
+          <el-date-picker v-model="form.expireTime" clearable type="datetime" value-format="YYYY-MM-DD HH:mm:ss" placeholder="璇烽�夋嫨杩囨湡鏃堕棿">
           </el-date-picker>
         </el-form-item>
         <el-form-item label="鐢ㄦ埛鏁伴噺" prop="accountCount">
@@ -124,7 +124,7 @@
           <el-input v-model="form.licenseNumber" placeholder="璇疯緭鍏ョ粺涓�绀句細淇$敤浠g爜" />
         </el-form-item>
         <el-form-item label="浼佷笟绠�浠�" prop="intro">
-          <el-input type="textarea" v-model="form.intro" placeholder="璇疯緭鍏ヤ紒涓氱畝浠�" />
+          <el-input v-model="form.intro" type="textarea" placeholder="璇疯緭鍏ヤ紒涓氱畝浠�" />
         </el-form-item>
         <el-form-item label="澶囨敞" prop="remark">
           <el-input v-model="form.remark" placeholder="璇疯緭鍏ュ娉�" />
@@ -182,8 +182,8 @@
   packageId: '',
   expireTime: '',
   accountCount: 0,
-  status: '0',
-}
+  status: '0'
+};
 const data = reactive<PageData<TenantForm, TenantQuery>>({
   form: { ...initFormData },
   queryParams: {
@@ -195,17 +195,17 @@
     companyName: ''
   },
   rules: {
-    id: [{ required: true, message: "id涓嶈兘涓虹┖", trigger: "blur" }],
-    tenantId: [{ required: true, message: "绉熸埛缂栧彿涓嶈兘涓虹┖", trigger: "blur" }],
-    contactUserName: [{ required: true, message: "鑱旂郴浜轰笉鑳戒负绌�", trigger: "blur" }],
-    contactPhone: [{ required: true, message: "鑱旂郴鐢佃瘽涓嶈兘涓虹┖", trigger: "blur" }],
-    companyName: [{ required: true, message: "浼佷笟鍚嶇О涓嶈兘涓虹┖", trigger: "blur" }],
+    id: [{ required: true, message: 'id涓嶈兘涓虹┖', trigger: 'blur' }],
+    tenantId: [{ required: true, message: '绉熸埛缂栧彿涓嶈兘涓虹┖', trigger: 'blur' }],
+    contactUserName: [{ required: true, message: '鑱旂郴浜轰笉鑳戒负绌�', trigger: 'blur' }],
+    contactPhone: [{ required: true, message: '鑱旂郴鐢佃瘽涓嶈兘涓虹┖', trigger: 'blur' }],
+    companyName: [{ required: true, message: '浼佷笟鍚嶇О涓嶈兘涓虹┖', trigger: 'blur' }],
     username: [
-      { required: true, message: "鐢ㄦ埛鍚嶄笉鑳戒负绌�", trigger: "blur" },
+      { required: true, message: '鐢ㄦ埛鍚嶄笉鑳戒负绌�', trigger: 'blur' },
       { min: 2, max: 20, message: '鐢ㄦ埛鍚嶇О闀垮害蹇呴』浠嬩簬 2 鍜� 20 涔嬮棿', trigger: 'blur' }
     ],
     password: [
-      { required: true, message: "瀵嗙爜涓嶈兘涓虹┖", trigger: "blur" },
+      { required: true, message: '瀵嗙爜涓嶈兘涓虹┖', trigger: 'blur' },
       { min: 5, max: 20, message: '鐢ㄦ埛瀵嗙爜闀垮害蹇呴』浠嬩簬 5 鍜� 20 涔嬮棿', trigger: 'blur' }
     ]
   }
@@ -215,9 +215,9 @@
 
 /** 鏌ヨ鎵�鏈夌鎴峰椁� */
 const getTenantPackage = async () => {
-  const res = await selectTenantPackage()
+  const res = await selectTenantPackage();
   packageList.value = res.data;
-}
+};
 
 /** 鏌ヨ绉熸埛鍒楄〃 */
 const getList = async () => {
@@ -226,60 +226,58 @@
   tenantList.value = res.rows;
   total.value = res.total;
   loading.value = false;
-}
+};
 
 // 绉熸埛濂楅鐘舵�佷慨鏀�
 const handleStatusChange = async (row: TenantVO) => {
-  let text = row.status === "0" ? "鍚敤" : "鍋滅敤";
+  let text = row.status === '0' ? '鍚敤' : '鍋滅敤';
   try {
     await proxy?.$modal.confirm('纭瑕�"' + text + '""' + row.companyName + '"绉熸埛鍚楋紵');
     await changeTenantStatus(row.id, row.tenantId, row.status);
-    proxy?.$modal.msgSuccess(text + "鎴愬姛");
+    proxy?.$modal.msgSuccess(text + '鎴愬姛');
   } catch {
-    row.status = row.status === "0" ? "1" : "0";
+    row.status = row.status === '0' ? '1' : '0';
   }
-
-
-}
+};
 
 // 鍙栨秷鎸夐挳
 const cancel = () => {
   reset();
   dialog.visible = false;
-}
+};
 
 // 琛ㄥ崟閲嶇疆
 const reset = () => {
   form.value = { ...initFormData };
   tenantFormRef.value?.resetFields();
-}
+};
 
 /** 鎼滅储鎸夐挳鎿嶄綔 */
 const handleQuery = () => {
   queryParams.value.pageNum = 1;
   getList();
-}
+};
 
 /** 閲嶇疆鎸夐挳鎿嶄綔 */
 const resetQuery = () => {
   queryFormRef.value?.resetFields();
   handleQuery();
-}
+};
 
 // 澶氶�夋閫変腑鏁版嵁
 const handleSelectionChange = (selection: TenantVO[]) => {
-  ids.value = selection.map(item => item.id);
+  ids.value = selection.map((item) => item.id);
   single.value = selection.length != 1;
   multiple.value = !selection.length;
-}
+};
 
 /** 鏂板鎸夐挳鎿嶄綔 */
 const handleAdd = () => {
   reset();
   getTenantPackage();
   dialog.visible = true;
-  dialog.title = "娣诲姞绉熸埛";
-}
+  dialog.title = '娣诲姞绉熸埛';
+};
 
 /** 淇敼鎸夐挳鎿嶄綔 */
 const handleUpdate = async (row?: TenantVO) => {
@@ -287,10 +285,10 @@
   await getTenantPackage();
   const _id = row?.id || ids.value[0];
   const res = await getTenant(_id);
-  Object.assign(form.value, res.data)
+  Object.assign(form.value, res.data);
   dialog.visible = true;
-  dialog.title = "淇敼绉熸埛";
-}
+  dialog.title = '淇敼绉熸埛';
+};
 
 /** 鎻愪氦鎸夐挳 */
 const submitForm = () => {
@@ -298,28 +296,26 @@
     if (valid) {
       buttonLoading.value = true;
       if (form.value.id) {
-        await updateTenant(form.value).finally(() => buttonLoading.value = false);
+        await updateTenant(form.value).finally(() => (buttonLoading.value = false));
       } else {
-        await addTenant(form.value).finally(() => buttonLoading.value = false);
+        await addTenant(form.value).finally(() => (buttonLoading.value = false));
       }
-      proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+      proxy?.$modal.msgSuccess('鎿嶄綔鎴愬姛');
       dialog.visible = false;
       await getList();
     }
   });
-}
+};
 
 /** 鍒犻櫎鎸夐挳鎿嶄綔 */
 const handleDelete = async (row?: TenantVO) => {
   const _ids = row?.id || ids.value;
-  await proxy?.$modal.confirm('鏄惁纭鍒犻櫎绉熸埛缂栧彿涓�"' + _ids + '"鐨勬暟鎹」锛�')
+  await proxy?.$modal.confirm('鏄惁纭鍒犻櫎绉熸埛缂栧彿涓�"' + _ids + '"鐨勬暟鎹」锛�');
   loading.value = true;
-  await delTenant(_ids).finally(() => loading.value = false);
+  await delTenant(_ids).finally(() => (loading.value = false));
   await getList();
-  proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
-
-
-}
+  proxy?.$modal.msgSuccess('鍒犻櫎鎴愬姛');
+};
 
 /** 鍚屾绉熸埛濂楅鎸夐挳鎿嶄綔 */
 const handleSyncTenantPackage = async (row: TenantVO) => {
@@ -328,20 +324,26 @@
     loading.value = true;
     await syncTenantPackage(row.tenantId, row.packageId);
     await getList();
-    proxy?.$modal.msgSuccess("鍚屾鎴愬姛");
-  } catch { return } finally {
+    proxy?.$modal.msgSuccess('鍚屾鎴愬姛');
+  } catch {
+    return;
+  } finally {
     loading.value = false;
   }
-}
+};
 
 /** 瀵煎嚭鎸夐挳鎿嶄綔 */
 const handleExport = () => {
-  proxy?.download('system/tenant/export', {
-    ...queryParams.value
-  }, `tenant_${new Date().getTime()}.xlsx`)
-}
+  proxy?.download(
+    'system/tenant/export',
+    {
+      ...queryParams.value
+    },
+    `tenant_${new Date().getTime()}.xlsx`
+  );
+};
 
 onMounted(() => {
   getList();
-})
+});
 </script>
diff --git a/src/views/system/tenantPackage/index.vue b/src/views/system/tenantPackage/index.vue
index 5311996..55cc7d7 100644
--- a/src/views/system/tenantPackage/index.vue
+++ b/src/views/system/tenantPackage/index.vue
@@ -1,11 +1,11 @@
 <template>
   <div class="p-2">
     <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
-      <div class="mb-[10px]" v-show="showSearch">
+      <div v-show="showSearch" class="mb-[10px]">
         <el-card shadow="hover">
-          <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
+          <el-form ref="queryFormRef" :model="queryParams" :inline="true">
             <el-form-item label="濂楅鍚嶇О" prop="packageName">
-              <el-input v-model="queryParams.packageName" placeholder="璇疯緭鍏ュ椁愬悕绉�" clearable style="width: 240px" @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.packageName" placeholder="璇疯緭鍏ュ椁愬悕绉�" clearable @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item>
               <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
@@ -20,28 +20,28 @@
       <template #header>
         <el-row :gutter="10" class="mb8">
           <el-col :span="1.5">
-            <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:tenantPackage:add']"> 鏂板 </el-button>
+            <el-button v-hasPermi="['system:tenantPackage:add']" type="primary" plain icon="Plus" @click="handleAdd"> 鏂板 </el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['system:tenantPackage:edit']">
+            <el-button v-hasPermi="['system:tenantPackage:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()">
               淇敼
             </el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['system:tenantPackage:remove']">
+            <el-button v-hasPermi="['system:tenantPackage:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">
               鍒犻櫎
             </el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:tenantPackage:export']">瀵煎嚭 </el-button>
+            <el-button v-hasPermi="['system:tenantPackage:export']" type="warning" plain icon="Download" @click="handleExport">瀵煎嚭 </el-button>
           </el-col>
-          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+          <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
         </el-row>
       </template>
 
       <el-table v-loading="loading" :data="tenantPackageList" @selection-change="handleSelectionChange">
         <el-table-column type="selection" width="55" align="center" />
-        <el-table-column label="绉熸埛濂楅id" align="center" prop="packageId" v-if="false" />
+        <el-table-column v-if="false" label="绉熸埛濂楅id" align="center" prop="packageId" />
         <el-table-column label="濂楅鍚嶇О" align="center" prop="packageName" />
         <el-table-column label="澶囨敞" align="center" prop="remark" />
         <el-table-column label="鐘舵��" align="center" prop="status">
@@ -52,20 +52,20 @@
         <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="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:tenantPackage:edit']"></el-button>
+              <el-button v-hasPermi="['system:tenantPackage:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
             </el-tooltip>
             <el-tooltip content="鍒犻櫎" placement="top">
-              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:tenantPackage:remove']"></el-button>
+              <el-button v-hasPermi="['system:tenantPackage:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></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" />
+      <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
     </el-card>
 
     <!-- 娣诲姞鎴栦慨鏀圭鎴峰椁愬璇濇 -->
-    <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
+    <el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
       <el-form ref="tenantPackageFormRef" :model="form" :rules="rules" label-width="80px">
         <el-form-item label="濂楅鍚嶇О" prop="packageName">
           <el-input v-model="form.packageName" placeholder="璇疯緭鍏ュ椁愬悕绉�" />
@@ -75,10 +75,10 @@
           <el-checkbox v-model="menuNodeAll" @change="handleCheckedTreeNodeAll($event, 'menu')">鍏ㄩ��/鍏ㄤ笉閫� </el-checkbox>
           <el-checkbox v-model="form.menuCheckStrictly" @change="handleCheckedTreeConnect($event, 'menu')">鐖跺瓙鑱斿姩 </el-checkbox>
           <el-tree
+            ref="menuTreeRef"
             class="tree-border"
             :data="menuOptions"
             show-checkbox
-            ref="menuTreeRef"
             node-key="id"
             :check-strictly="!form.menuCheckStrictly"
             empty-text="鍔犺浇涓紝璇风◢鍊�"
@@ -107,11 +107,11 @@
   addTenantPackage,
   updateTenantPackage,
   changePackageStatus
-} from "@/api/system/tenantPackage";
-import { treeselect as menuTreeselect, tenantPackageMenuTreeselect } from "@/api/system/menu";
-import { TenantPkgForm, TenantPkgQuery, TenantPkgVO } from "@/api/system/tenantPackage/types";
-import { MenuTreeOption } from "@/api/system/menu/types";
-import to from "await-to-js";
+} from '@/api/system/tenantPackage';
+import { treeselect as menuTreeselect, tenantPackageMenuTreeselect } from '@/api/system/menu';
+import { TenantPkgForm, TenantPkgQuery, TenantPkgVO } from '@/api/system/tenantPackage/types';
+import { MenuTreeOption } from '@/api/system/menu/types';
+import to from 'await-to-js';
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
@@ -133,15 +133,14 @@
 
 const dialog = reactive<DialogOption>({
   visible: false,
-  title: ""
+  title: ''
 });
-
 
 const initFormData: TenantPkgForm = {
   packageId: undefined,
-  packageName: "",
-  menuIds: "",
-  remark: "",
+  packageName: '',
+  menuIds: '',
+  remark: '',
   menuCheckStrictly: true
 };
 const data = reactive<PageData<TenantPkgForm, TenantPkgQuery>>({
@@ -149,11 +148,11 @@
   queryParams: {
     pageNum: 1,
     pageSize: 10,
-    packageName: ""
+    packageName: ''
   },
   rules: {
-    packageId: [{ required: true, message: "绉熸埛濂楅id涓嶈兘涓虹┖", trigger: "blur" }],
-    packageName: [{ required: true, message: "濂楅鍚嶇О涓嶈兘涓虹┖", trigger: "blur" }]
+    packageId: [{ required: true, message: '绉熸埛濂楅id涓嶈兘涓虹┖', trigger: 'blur' }],
+    packageName: [{ required: true, message: '濂楅鍚嶇О涓嶈兘涓虹┖', trigger: 'blur' }]
   }
 });
 
@@ -195,13 +194,13 @@
 
 // 绉熸埛濂楅鐘舵�佷慨鏀�
 const handleStatusChange = async (row: TenantPkgVO) => {
-  let text = row.status === "0" ? "鍚敤" : "鍋滅敤";
-  const [err] = await to(proxy?.$modal.confirm("纭瑕乗"" + text + "\"\"" + row.packageName + "\"濂楅鍚楋紵") as Promise<any>);
+  let text = row.status === '0' ? '鍚敤' : '鍋滅敤';
+  const [err] = await to(proxy?.$modal.confirm('纭瑕�"' + text + '""' + row.packageName + '"濂楅鍚楋紵') as Promise<any>);
   if (err) {
-    row.status = row.status === "0" ? "1" : "0";
+    row.status = row.status === '0' ? '1' : '0';
   } else {
     await changePackageStatus(row.packageId, row.status);
-    proxy?.$modal.msgSuccess(text + "鎴愬姛");
+    proxy?.$modal.msgSuccess(text + '鎴愬姛');
   }
 };
 
@@ -234,14 +233,14 @@
 
 // 澶氶�夋閫変腑鏁版嵁
 const handleSelectionChange = (selection: TenantPkgVO[]) => {
-  ids.value = selection.map(item => item.packageId);
+  ids.value = selection.map((item) => item.packageId);
   single.value = selection.length != 1;
   multiple.value = !selection.length;
 };
 
 // 鏍戞潈闄愶紙灞曞紑/鎶樺彔锛�
 const handleCheckedTreeExpand = (value: CheckboxValueType, type: string) => {
-  if (type == "menu") {
+  if (type == 'menu') {
     let treeList = menuOptions.value;
     for (let i = 0; i < treeList.length; i++) {
       if (menuTreeRef.value) {
@@ -253,14 +252,14 @@
 
 // 鏍戞潈闄愶紙鍏ㄩ��/鍏ㄤ笉閫夛級
 const handleCheckedTreeNodeAll = (value: CheckboxValueType, type: string) => {
-  if (type == "menu") {
-    menuTreeRef.value?.setCheckedNodes(value ? menuOptions.value as any : []);
+  if (type == 'menu') {
+    menuTreeRef.value?.setCheckedNodes(value ? (menuOptions.value as any) : []);
   }
 };
 
 // 鏍戞潈闄愶紙鐖跺瓙鑱斿姩锛�
 const handleCheckedTreeConnect = (value: CheckboxValueType, type: string) => {
-  if (type == "menu") {
+  if (type == 'menu') {
     form.value.menuCheckStrictly = value as boolean;
   }
 };
@@ -270,7 +269,7 @@
   reset();
   getMenuTreeselect();
   dialog.visible = true;
-  dialog.title = "娣诲姞绉熸埛濂楅";
+  dialog.title = '娣诲姞绉熸埛濂楅';
 };
 
 /** 淇敼鎸夐挳鎿嶄綔 */
@@ -281,7 +280,7 @@
   form.value = response.data;
   const res = await getPackageMenuTreeselect(_packageId);
   dialog.visible = true;
-  dialog.title = "淇敼绉熸埛濂楅";
+  dialog.title = '淇敼绉熸埛濂楅';
   res.data.checkedKeys.forEach((v) => {
     nextTick(() => {
       menuTreeRef.value?.setChecked(v, true, false);
@@ -296,11 +295,11 @@
       buttonLoading.value = true;
       form.value.menuIds = getMenuAllCheckedKeys();
       if (form.value.packageId != null) {
-        await updateTenantPackage(form.value).finally(() => buttonLoading.value = false);
+        await updateTenantPackage(form.value).finally(() => (buttonLoading.value = false));
       } else {
-        await addTenantPackage(form.value).finally(() => buttonLoading.value = false);
+        await addTenantPackage(form.value).finally(() => (buttonLoading.value = false));
       }
-      proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+      proxy?.$modal.msgSuccess('鎿嶄綔鎴愬姛');
       dialog.visible = false;
       await getList();
     }
@@ -310,20 +309,24 @@
 /** 鍒犻櫎鎸夐挳鎿嶄綔 */
 const handleDelete = async (row?: TenantPkgVO) => {
   const _packageIds = row?.packageId || ids.value;
-  await proxy?.$modal.confirm("鏄惁纭鍒犻櫎绉熸埛濂楅缂栧彿涓篭"" + _packageIds + "\"鐨勬暟鎹」锛�").finally(() => {
+  await proxy?.$modal.confirm('鏄惁纭鍒犻櫎绉熸埛濂楅缂栧彿涓�"' + _packageIds + '"鐨勬暟鎹」锛�').finally(() => {
     loading.value = false;
   });
   await delTenantPackage(_packageIds);
   loading.value = true;
   await getList();
-  proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+  proxy?.$modal.msgSuccess('鍒犻櫎鎴愬姛');
 };
 
 /** 瀵煎嚭鎸夐挳鎿嶄綔 */
 const handleExport = () => {
-  proxy?.download("system/tenantPackage/export", {
-    ...queryParams.value
-  }, `tenantPackage_${new Date().getTime()}.xlsx`);
+  proxy?.download(
+    'system/tenantPackage/export',
+    {
+      ...queryParams.value
+    },
+    `tenantPackage_${new Date().getTime()}.xlsx`
+  );
 };
 
 onMounted(() => {
diff --git a/src/views/system/user/authRole.vue b/src/views/system/user/authRole.vue
index e869ba3..87d2334 100644
--- a/src/views/system/user/authRole.vue
+++ b/src/views/system/user/authRole.vue
@@ -2,7 +2,7 @@
   <div class="p-2">
     <div class="panel">
       <h4 class="panel-title">鍩烘湰淇℃伅</h4>
-      <el-form :model="form" label-width="80px" :inline="true">
+      <el-form :model="form" :inline="true">
         <el-row :gutter="10">
           <el-col :span="2.5">
             <el-form-item label="鐢ㄦ埛鏄电О" prop="nickName">
@@ -21,12 +21,12 @@
       <h4 class="panel-title">瑙掕壊淇℃伅</h4>
       <div>
         <el-table
+          ref="tableRef"
           v-loading="loading"
           :row-key="getRowKey"
-          @row-click="clickRow"
-          ref="tableRef"
-          @selection-change="handleSelectionChange"
           :data="roles.slice((pageNum - 1) * pageSize, pageNum * pageSize)"
+          @row-click="clickRow"
+          @selection-change="handleSelectionChange"
         >
           <el-table-column label="搴忓彿" width="55" type="index" align="center">
             <template #default="scope">
@@ -43,8 +43,8 @@
             </template>
           </el-table-column>
         </el-table>
-        <pagination v-show="total > 0" :total="total" v-model:page="pageNum" v-model:limit="pageSize" />
-        <div style="text-align: center;margin-left:-120px;margin-top:30px;">
+        <pagination v-show="total > 0" v-model:page="pageNum" v-model:limit="pageSize" :total="total" />
+        <div style="text-align: center; margin-left: -120px; margin-top: 30px">
           <el-button type="primary" @click="submitForm()">鎻愪氦</el-button>
           <el-button @click="close()">杩斿洖</el-button>
         </div>
@@ -55,9 +55,11 @@
 </template>
 
 <script setup name="AuthRole" lang="ts">
-import { RoleVO } from "@/api/system/role/types";
-import { getAuthRole, updateAuthRole } from "@/api/system/user";
-import { UserForm } from "@/api/system/user/types";
+import { RoleVO } from '@/api/system/role/types';
+import { getAuthRole, updateAuthRole } from '@/api/system/user';
+import { UserForm } from '@/api/system/user/types';
+import { RouteLocationNormalized } from 'vue-router';
+import { parseTime } from '@/utils/ruoyi';
 
 const route = useRoute();
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@@ -70,7 +72,7 @@
 const roles = ref<RoleVO[]>([]);
 const form = ref<Partial<UserForm>>({
   nickName: undefined,
-  userName: "",
+  userName: '',
   userId: undefined
 });
 
@@ -83,7 +85,7 @@
 };
 /** 澶氶�夋閫変腑鏁版嵁 */
 const handleSelectionChange = (selection: RoleVO[]) => {
-  roleIds.value = selection.map(item => item.roleId);
+  roleIds.value = selection.map((item) => item.roleId);
 };
 /** 淇濆瓨閫変腑鐨勬暟鎹紪鍙� */
 const getRowKey = (row: RoleVO): string => {
@@ -91,15 +93,25 @@
 };
 /** 鍏抽棴鎸夐挳 */
 const close = () => {
-  const obj = { path: "/system/user" };
+  const obj: RouteLocationNormalized = {
+    fullPath: '',
+    hash: '',
+    matched: [],
+    meta: undefined,
+    name: undefined,
+    params: undefined,
+    query: undefined,
+    redirectedFrom: undefined,
+    path: '/system/user'
+  };
   proxy?.$tab.closeOpenPage(obj);
 };
 /** 鎻愪氦鎸夐挳 */
 const submitForm = async () => {
   const userId = form.value.userId;
-  const rIds = roleIds.value.join(",");
+  const rIds = roleIds.value.join(',');
   await updateAuthRole({ userId: userId as string, roleIds: rIds });
-  proxy?.$modal.msgSuccess("鎺堟潈鎴愬姛");
+  proxy?.$modal.msgSuccess('鎺堟潈鎴愬姛');
   close();
 };
 
@@ -112,7 +124,7 @@
     Object.assign(roles.value, res.data.roles);
     total.value = roles.value.length;
     await nextTick(() => {
-      roles.value.forEach(row => {
+      roles.value.forEach((row) => {
         if (row?.flag) {
           tableRef.value?.toggleRowSelection(row, true);
         }
diff --git a/src/views/system/user/index.vue b/src/views/system/user/index.vue
index 100b5a7..c1bdcf9 100644
--- a/src/views/system/user/index.vue
+++ b/src/views/system/user/index.vue
@@ -6,8 +6,8 @@
         <el-card shadow="hover">
           <el-input v-model="deptName" placeholder="璇疯緭鍏ラ儴闂ㄥ悕绉�" prefix-icon="Search" clearable />
           <el-tree
-            class="mt-2"
             ref="deptTreeRef"
+            class="mt-2"
             node-key="id"
             :data="deptOptions"
             :props="{ label: 'label', children: 'children' }"
@@ -21,28 +21,22 @@
       </el-col>
       <el-col :lg="20" :xs="24">
         <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
-          <div class="mb-[10px]" v-show="showSearch">
+          <div v-show="showSearch" class="mb-[10px]">
             <el-card shadow="hover">
-              <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="68px">
+              <el-form ref="queryFormRef" :model="queryParams" :inline="true">
                 <el-form-item label="鐢ㄦ埛鍚嶇О" prop="userName">
-                  <el-input v-model="queryParams.userName" placeholder="璇疯緭鍏ョ敤鎴峰悕绉�" clearable style="width: 240px" @keyup.enter="handleQuery" />
+                  <el-input v-model="queryParams.userName" placeholder="璇疯緭鍏ョ敤鎴峰悕绉�" clearable @keyup.enter="handleQuery" />
                 </el-form-item>
                 <el-form-item label="鎵嬫満鍙风爜" prop="phonenumber">
-                  <el-input
-                    v-model="queryParams.phonenumber"
-                    placeholder="璇疯緭鍏ユ墜鏈哄彿鐮�"
-                    clearable
-                    style="width: 240px"
-                    @keyup.enter="handleQuery"
-                  />
+                  <el-input v-model="queryParams.phonenumber" placeholder="璇疯緭鍏ユ墜鏈哄彿鐮�" clearable @keyup.enter="handleQuery" />
                 </el-form-item>
 
                 <el-form-item label="鐘舵��" prop="status">
-                  <el-select v-model="queryParams.status" placeholder="鐢ㄦ埛鐘舵��" clearable style="width: 240px">
+                  <el-select v-model="queryParams.status" placeholder="鐢ㄦ埛鐘舵��" clearable>
                     <el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
                   </el-select>
                 </el-form-item>
-                <el-form-item label="鍒涘缓鏃堕棿" style="width: 308px;">
+                <el-form-item label="鍒涘缓鏃堕棿" style="width: 308px">
                   <el-date-picker
                     v-model="dateRange"
                     value-format="YYYY-MM-DD"
@@ -53,8 +47,8 @@
                   ></el-date-picker>
                 </el-form-item>
                 <el-form-item>
-                  <el-button type="primary" @click="handleQuery" icon="Search">鎼滅储</el-button>
-                  <el-button @click="resetQuery" icon="Refresh">閲嶇疆</el-button>
+                  <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button>
+                  <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button>
                 </el-form-item>
               </el-form>
             </el-card>
@@ -65,15 +59,15 @@
           <template #header>
             <el-row :gutter="10">
               <el-col :span="1.5">
-                <el-button type="primary" plain @click="handleAdd()" v-has-permi="['system:user:add']" icon="Plus">鏂板</el-button>
+                <el-button v-has-permi="['system:user:add']" type="primary" plain icon="Plus" @click="handleAdd()">鏂板</el-button>
               </el-col>
               <el-col :span="1.5">
-                <el-button type="success" plain @click="handleUpdate()" :disabled="single" v-has-permi="['system:user:add']" icon="Edit">
+                <el-button v-has-permi="['system:user:add']" type="success" plain :disabled="single" icon="Edit" @click="handleUpdate()">
                   淇敼
                 </el-button>
               </el-col>
               <el-col :span="1.5">
-                <el-button type="danger" plain @click="handleDelete()" :disabled="multiple" v-has-permi="['system:user:delete']" icon="Delete">
+                <el-button v-has-permi="['system:user:delete']" type="danger" plain :disabled="multiple" icon="Delete" @click="handleDelete()">
                   鍒犻櫎
                 </el-button>
               </el-col>
@@ -85,38 +79,31 @@
                   ></el-button>
                   <template #dropdown>
                     <el-dropdown-menu>
-                      <el-dropdown-item @click="importTemplate" icon="Download">涓嬭浇妯℃澘</el-dropdown-item>
-                      <el-dropdown-item @click="handleImport" icon="Top"> 瀵煎叆鏁版嵁</el-dropdown-item>
-                      <el-dropdown-item @click="handleExport" icon="Download"> 瀵煎嚭鏁版嵁</el-dropdown-item>
+                      <el-dropdown-item icon="Download" @click="importTemplate">涓嬭浇妯℃澘</el-dropdown-item>
+                      <el-dropdown-item icon="Top" @click="handleImport"> 瀵煎叆鏁版嵁</el-dropdown-item>
+                      <el-dropdown-item icon="Download" @click="handleExport"> 瀵煎嚭鏁版嵁</el-dropdown-item>
                     </el-dropdown-menu>
                   </template>
                 </el-dropdown>
               </el-col>
-              <right-toolbar v-model:showSearch="showSearch" @queryTable="getList" :columns="columns" :search="true"></right-toolbar>
+              <right-toolbar v-model:showSearch="showSearch" :columns="columns" :search="true" @query-table="getList"></right-toolbar>
             </el-row>
           </template>
 
           <el-table v-loading="loading" :data="userList" @selection-change="handleSelectionChange">
             <el-table-column type="selection" width="50" align="center" />
-            <el-table-column label="鐢ㄦ埛缂栧彿" align="center" key="userId" prop="userId" v-if="columns[0].visible" />
-            <el-table-column label="鐢ㄦ埛鍚嶇О" align="center" key="userName" prop="userName" v-if="columns[1].visible" :show-overflow-tooltip="true" />
-            <el-table-column label="鐢ㄦ埛鏄电О" align="center" key="nickName" prop="nickName" v-if="columns[2].visible" :show-overflow-tooltip="true" />
-            <el-table-column
-              label="閮ㄩ棬"
-              align="center"
-              key="deptName"
-              prop="dept.deptName"
-              v-if="columns[3].visible"
-              :show-overflow-tooltip="true"
-            />
-            <el-table-column label="鎵嬫満鍙风爜" align="center" key="phonenumber" prop="phonenumber" v-if="columns[4].visible" width="120" />
-            <el-table-column label="鐘舵��" align="center" key="status" v-if="columns[5].visible">
+            <el-table-column v-if="columns[0].visible" key="userId" label="鐢ㄦ埛缂栧彿" align="center" prop="userId" />
+            <el-table-column v-if="columns[1].visible" key="userName" label="鐢ㄦ埛鍚嶇О" align="center" prop="userName" :show-overflow-tooltip="true" />
+            <el-table-column v-if="columns[2].visible" key="nickName" label="鐢ㄦ埛鏄电О" align="center" prop="nickName" :show-overflow-tooltip="true" />
+            <el-table-column v-if="columns[3].visible" key="deptName" label="閮ㄩ棬" align="center" prop="deptName" :show-overflow-tooltip="true" />
+            <el-table-column v-if="columns[4].visible" key="phonenumber" label="鎵嬫満鍙风爜" align="center" prop="phonenumber" width="120" />
+            <el-table-column v-if="columns[5].visible" key="status" label="鐘舵��" align="center">
               <template #default="scope">
                 <el-switch v-model="scope.row.status" active-value="0" inactive-value="1" @change="handleStatusChange(scope.row)"></el-switch>
               </template>
             </el-table-column>
 
-            <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" v-if="columns[6].visible" width="160">
+            <el-table-column v-if="columns[6].visible" label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="160">
               <template #default="scope">
                 <span>{{ scope.row.createTime }}</span>
               </template>
@@ -124,19 +111,19 @@
 
             <el-table-column label="鎿嶄綔" fixed="right" width="180" class-name="small-padding fixed-width">
               <template #default="scope">
-                <el-tooltip content="淇敼" placement="top" v-if="scope.row.userId !== 1">
-                  <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['system:user:edit']"></el-button>
+                <el-tooltip v-if="scope.row.userId !== 1" content="淇敼" placement="top">
+                  <el-button v-hasPermi="['system:user:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
                 </el-tooltip>
-                <el-tooltip content="鍒犻櫎" placement="top" v-if="scope.row.userId !== 1">
-                  <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['system:user:remove']"></el-button>
+                <el-tooltip v-if="scope.row.userId !== 1" content="鍒犻櫎" placement="top">
+                  <el-button v-hasPermi="['system:user:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
                 </el-tooltip>
 
-                <el-tooltip content="閲嶇疆瀵嗙爜" placement="top" v-if="scope.row.userId !== 1">
-                  <el-button link type="primary" icon="Key" @click="handleResetPwd(scope.row)" v-hasPermi="['system:user:resetPwd']"></el-button>
+                <el-tooltip v-if="scope.row.userId !== 1" content="閲嶇疆瀵嗙爜" placement="top">
+                  <el-button v-hasPermi="['system:user:resetPwd']" link type="primary" icon="Key" @click="handleResetPwd(scope.row)"></el-button>
                 </el-tooltip>
 
-                <el-tooltip content="鍒嗛厤瑙掕壊" placement="top" v-if="scope.row.userId !== 1">
-                  <el-button link type="primary" icon="CircleCheck" @click="handleAuthRole(scope.row)" v-hasPermi="['system:user:edit']"></el-button>
+                <el-tooltip v-if="scope.row.userId !== 1" content="鍒嗛厤瑙掕壊" placement="top">
+                  <el-button v-hasPermi="['system:user:edit']" link type="primary" icon="CircleCheck" @click="handleAuthRole(scope.row)"></el-button>
                 </el-tooltip>
               </template>
             </el-table-column>
@@ -144,9 +131,9 @@
 
           <pagination
             v-show="total > 0"
-            :total="total"
             v-model:page="queryParams.pageNum"
             v-model:limit="queryParams.pageSize"
+            :total="total"
             @pagination="getList"
           />
         </el-card>
@@ -154,8 +141,8 @@
     </el-row>
 
     <!-- 娣诲姞鎴栦慨鏀圭敤鎴烽厤缃璇濇 -->
-    <el-dialog ref="formDialogRef" :title="dialog.title" v-model="dialog.visible" width="600px" append-to-body @close="closeDialog">
-      <el-form :model="form" :rules="rules" ref="userFormRef" label-width="80px">
+    <el-dialog ref="formDialogRef" v-model="dialog.visible" :title="dialog.title" width="600px" append-to-body @close="closeDialog">
+      <el-form ref="userFormRef" :model="form" :rules="rules" label-width="80px">
         <el-row>
           <el-col :span="12">
             <el-form-item label="鐢ㄦ埛鏄电О" prop="nickName">
@@ -171,6 +158,7 @@
                 value-key="id"
                 placeholder="璇烽�夋嫨褰掑睘閮ㄩ棬"
                 check-strictly
+                @change="handleDeptChange"
               />
             </el-form-item>
           </el-col>
@@ -210,8 +198,7 @@
           <el-col :span="12">
             <el-form-item label="鐘舵��">
               <el-radio-group v-model="form.status">
-                <el-radio v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.value">{{
-                  dict.label }}</el-radio>
+                <el-radio v-for="dict in sys_normal_disable" :key="dict.value" :value="dict.value">{{ dict.label }}</el-radio>
               </el-radio-group>
             </el-form-item>
           </el-col>
@@ -231,8 +218,8 @@
             </el-form-item>
           </el-col>
           <el-col :span="12">
-            <el-form-item label="瑙掕壊">
-              <el-select v-model="form.roleIds" multiple placeholder="璇烽�夋嫨">
+            <el-form-item label="瑙掕壊" prop="roleIds">
+              <el-select v-model="form.roleIds" filterable multiple placeholder="璇烽�夋嫨">
                 <el-option
                   v-for="item in roleOptions"
                   :key="item.roleId"
@@ -261,7 +248,7 @@
     </el-dialog>
 
     <!-- 鐢ㄦ埛瀵煎叆瀵硅瘽妗� -->
-    <el-dialog :title="upload.title" v-model="upload.open" width="400px" append-to-body>
+    <el-dialog v-model="upload.open" :title="upload.title" width="400px" append-to-body>
       <el-upload
         ref="uploadRef"
         :limit="1"
@@ -282,7 +269,7 @@
           <div class="text-center el-upload__tip">
             <div class="el-upload__tip"><el-checkbox v-model="upload.updateSupport" />鏄惁鏇存柊宸茬粡瀛樺湪鐨勭敤鎴锋暟鎹�</div>
             <span>浠呭厑璁稿鍏ls銆亁lsx鏍煎紡鏂囦欢銆�</span>
-            <el-link type="primary" :underline="false" style="font-size:12px;vertical-align: baseline;" @click="importTemplate">涓嬭浇妯℃澘</el-link>
+            <el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline" @click="importTemplate">涓嬭浇妯℃澘</el-link>
           </div>
         </template>
       </el-upload>
@@ -297,22 +284,22 @@
 </template>
 
 <script setup name="User" lang="ts">
-import api from "@/api/system/user"
+import api from '@/api/system/user';
 import { UserForm, UserQuery, UserVO } from '@/api/system/user/types';
-import { treeselect } from "@/api/system/dept";
-import { DeptVO } from "@/api/system/dept/types";
-import { RoleVO } from "@/api/system/role/types";
-import { PostVO } from "@/api/system/post/types";
-import { to } from "await-to-js";
-import { globalHeaders } from "@/utils/request";
+import { DeptVO } from '@/api/system/dept/types';
+import { RoleVO } from '@/api/system/role/types';
+import { PostQuery, PostVO } from '@/api/system/post/types';
+import { treeselect } from '@/api/system/dept';
+import { globalHeaders } from '@/utils/request';
+import { to } from 'await-to-js';
+import { optionselect } from '@/api/system/post';
 
 const router = useRouter();
-const { proxy } = getCurrentInstance() as ComponentInternalInstance
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 const { sys_normal_disable, sys_user_sex } = toRefs<any>(proxy?.useDict('sys_normal_disable', 'sys_user_sex'));
-
 const userList = ref<UserVO[]>();
 const loading = ref(true);
-const showSearch = ref(true)
+const showSearch = ref(true);
 const ids = ref<Array<number | string>>([]);
 const single = ref(true);
 const multiple = ref(true);
@@ -320,7 +307,7 @@
 const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
 const deptName = ref('');
 const deptOptions = ref<DeptVO[]>([]);
-const initPassword = ref<String>('');
+const initPassword = ref<string>('');
 const postOptions = ref<PostVO[]>([]);
 const roleOptions = ref<RoleVO[]>([]);
 /*** 鐢ㄦ埛瀵煎叆鍙傛暟 */
@@ -328,7 +315,7 @@
   // 鏄惁鏄剧ず寮瑰嚭灞傦紙鐢ㄦ埛瀵煎叆锛�
   open: false,
   // 寮瑰嚭灞傛爣棰橈紙鐢ㄦ埛瀵煎叆锛�
-  title: "",
+  title: '',
   // 鏄惁绂佺敤涓婁紶
   isUploading: false,
   // 鏄惁鏇存柊宸茬粡瀛樺湪鐨勭敤鎴锋暟鎹�
@@ -336,19 +323,18 @@
   // 璁剧疆涓婁紶鐨勮姹傚ご閮�
   headers: globalHeaders(),
   // 涓婁紶鐨勫湴鍧�
-  url: import.meta.env.VITE_APP_BASE_API + "/system/user/importData"
-})
+  url: import.meta.env.VITE_APP_BASE_API + '/system/user/importData'
+});
 // 鍒楁樉闅愪俊鎭�
 const columns = ref<FieldOption[]>([
-  { key: 0, label: `鐢ㄦ埛缂栧彿`, visible: false,children: [] },
-  { key: 1, label: `鐢ㄦ埛鍚嶇О`, visible: true,children: [] },
-  { key: 2, label: `鐢ㄦ埛鏄电О`, visible: true,children: [] },
-  { key: 3, label: `閮ㄩ棬`, visible: true,children: [] },
-  { key: 4, label: `鎵嬫満鍙风爜`, visible: true,children: [] },
-  { key: 5, label: `鐘舵�乣, visible: true,children: [] },
-  { key: 6, label: `鍒涘缓鏃堕棿`, visible: true,children: [] }
-])
-
+  { key: 0, label: `鐢ㄦ埛缂栧彿`, visible: false, children: [] },
+  { key: 1, label: `鐢ㄦ埛鍚嶇О`, visible: true, children: [] },
+  { key: 2, label: `鐢ㄦ埛鏄电О`, visible: true, children: [] },
+  { key: 3, label: `閮ㄩ棬`, visible: true, children: [] },
+  { key: 4, label: `鎵嬫満鍙风爜`, visible: true, children: [] },
+  { key: 5, label: `鐘舵�乣, visible: true, children: [] },
+  { key: 6, label: `鍒涘缓鏃堕棿`, visible: true, children: [] }
+]);
 
 const deptTreeRef = ref<ElTreeInstance>();
 const queryFormRef = ref<ElFormInstance>();
@@ -370,12 +356,13 @@
   phonenumber: undefined,
   email: undefined,
   sex: undefined,
-  status: "0",
+  status: '0',
   remark: '',
   postIds: [],
   roleIds: []
-}
-const data = reactive<PageData<UserForm, UserQuery>>({
+};
+
+const initData: PageData<UserForm, UserQuery> = {
   form: { ...initFormData },
   queryParams: {
     pageNum: 1,
@@ -383,27 +370,61 @@
     userName: '',
     phonenumber: '',
     status: '',
-    deptId: ''
+    deptId: '',
+    roleId: ''
   },
   rules: {
-    userName: [{ required: true, message: "鐢ㄦ埛鍚嶇О涓嶈兘涓虹┖", trigger: "blur" }, { min: 2, max: 20, message: "鐢ㄦ埛鍚嶇О闀垮害蹇呴』浠嬩簬 2 鍜� 20 涔嬮棿", trigger: "blur" }],
-    nickName: [{ required: true, message: "鐢ㄦ埛鏄电О涓嶈兘涓虹┖", trigger: "blur" }],
-    password: [{ required: true, message: "鐢ㄦ埛瀵嗙爜涓嶈兘涓虹┖", trigger: "blur" }, { min: 5, max: 20, message: "鐢ㄦ埛瀵嗙爜闀垮害蹇呴』浠嬩簬 5 鍜� 20 涔嬮棿", trigger: "blur" }],
-    email: [{ type: "email", message: "璇疯緭鍏ユ纭殑閭鍦板潃", trigger: ["blur", "change"] }],
-    phonenumber: [{ pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "璇疯緭鍏ユ纭殑鎵嬫満鍙风爜", trigger: "blur" }]
+    userName: [
+      { required: true, message: '鐢ㄦ埛鍚嶇О涓嶈兘涓虹┖', trigger: 'blur' },
+      {
+        min: 2,
+        max: 20,
+        message: '鐢ㄦ埛鍚嶇О闀垮害蹇呴』浠嬩簬 2 鍜� 20 涔嬮棿',
+        trigger: 'blur'
+      }
+    ],
+    nickName: [{ required: true, message: '鐢ㄦ埛鏄电О涓嶈兘涓虹┖', trigger: 'blur' }],
+    password: [
+      { required: true, message: '鐢ㄦ埛瀵嗙爜涓嶈兘涓虹┖', trigger: 'blur' },
+      {
+        min: 5,
+        max: 20,
+        message: '鐢ㄦ埛瀵嗙爜闀垮害蹇呴』浠嬩簬 5 鍜� 20 涔嬮棿',
+        trigger: 'blur'
+      },
+      { pattern: /^[^<>"'|\\]+$/, message: '涓嶈兘鍖呭惈闈炴硶瀛楃锛�< > " \' \\\ |', trigger: 'blur' }
+    ],
+    email: [
+      {
+        type: 'email',
+        message: '璇疯緭鍏ユ纭殑閭鍦板潃',
+        trigger: ['blur', 'change']
+      }
+    ],
+    phonenumber: [
+      {
+        pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/,
+        message: '璇疯緭鍏ユ纭殑鎵嬫満鍙风爜',
+        trigger: 'blur'
+      }
+    ],
+    roleIds: [{ required: true, message: '鐢ㄦ埛瑙掕壊涓嶈兘涓虹┖', trigger: 'blur' }]
   }
-})
+};
+const data = reactive<PageData<UserForm, UserQuery>>(initData);
 
-const { queryParams, form, rules } = toRefs<PageData<UserForm, UserQuery>>(data)
+const { queryParams, form, rules } = toRefs<PageData<UserForm, UserQuery>>(data);
 
 /** 閫氳繃鏉′欢杩囨护鑺傜偣  */
 const filterNode = (value: string, data: any) => {
-  if (!value) return true
-  return data.label.indexOf(value) !== -1
-}
+  if (!value) return true;
+  return data.label.indexOf(value) !== -1;
+};
 /** 鏍规嵁鍚嶇О绛涢�夐儴闂ㄦ爲 */
 watchEffect(
-  () => { deptTreeRef.value?.filter(deptName.value); },
+  () => {
+    deptTreeRef.value?.filter(deptName.value);
+  },
   {
     flush: 'post' // watchEffect浼氬湪DOM鎸傝浇鎴栬�呮洿鏂颁箣鍓嶅氨浼氳Е鍙戯紝姝ゅ睘鎬ф帶鍒跺湪DOM鍏冪礌鏇存柊鍚庤繍琛�
   }
@@ -422,29 +443,28 @@
   loading.value = false;
   userList.value = res.rows;
   total.value = res.total;
-}
+};
 
 /** 鑺傜偣鍗曞嚮浜嬩欢 */
 const handleNodeClick = (data: DeptVO) => {
   queryParams.value.deptId = data.id;
-  handleQuery()
-}
-
+  handleQuery();
+};
 
 /** 鎼滅储鎸夐挳鎿嶄綔 */
 const handleQuery = () => {
-  queryParams.value.pageNum = 1
-  getList()
-}
+  queryParams.value.pageNum = 1;
+  getList();
+};
 /** 閲嶇疆鎸夐挳鎿嶄綔 */
 const resetQuery = () => {
-  dateRange.value = ['', '']
+  dateRange.value = ['', ''];
   queryFormRef.value?.resetFields();
   queryParams.value.pageNum = 1;
   queryParams.value.deptId = undefined;
   deptTreeRef.value?.setCurrentKey(undefined);
   handleQuery();
-}
+};
 
 /** 鍒犻櫎鎸夐挳鎿嶄綔 */
 const handleDelete = async (row?: UserVO) => {
@@ -453,78 +473,90 @@
   if (!err) {
     await api.delUser(userIds);
     await getList();
-    proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛");
+    proxy?.$modal.msgSuccess('鍒犻櫎鎴愬姛');
   }
-}
+};
 
 /** 鐢ㄦ埛鐘舵�佷慨鏀�  */
 const handleStatusChange = async (row: UserVO) => {
-  let text = row.status === "0" ? "鍚敤" : "鍋滅敤"
+  let text = row.status === '0' ? '鍚敤' : '鍋滅敤';
   try {
     await proxy?.$modal.confirm('纭瑕�"' + text + '""' + row.userName + '"鐢ㄦ埛鍚�?');
     await api.changeUserStatus(row.userId, row.status);
-    proxy?.$modal.msgSuccess(text + "鎴愬姛");
+    proxy?.$modal.msgSuccess(text + '鎴愬姛');
   } catch (err) {
-    row.status = row.status === "0" ? "1" : "0";
+    row.status = row.status === '0' ? '1' : '0';
   }
-}
+};
 /** 璺宠浆瑙掕壊鍒嗛厤 */
 const handleAuthRole = (row: UserVO) => {
   const userId = row.userId;
-  router.push("/system/user-auth/role/" + userId);
-}
+  router.push('/system/user-auth/role/' + userId);
+};
 
 /** 閲嶇疆瀵嗙爜鎸夐挳鎿嶄綔 */
 const handleResetPwd = async (row: UserVO) => {
-  const [err, res] = await to(ElMessageBox.prompt('璇疯緭鍏�"' + row.userName + '"鐨勬柊瀵嗙爜', "鎻愮ず", {
-    confirmButtonText: "纭畾",
-    cancelButtonText: "鍙栨秷",
-    closeOnClickModal: false,
-    inputPattern: /^.{5,20}$/,
-    inputErrorMessage: "鐢ㄦ埛瀵嗙爜闀垮害蹇呴』浠嬩簬 5 鍜� 20 涔嬮棿",
-  }))
-  if (!err) {
+  const [err, res] = await to(
+    ElMessageBox.prompt('璇疯緭鍏�"' + row.userName + '"鐨勬柊瀵嗙爜', '鎻愮ず', {
+      confirmButtonText: '纭畾',
+      cancelButtonText: '鍙栨秷',
+      closeOnClickModal: false,
+      inputPattern: /^.{5,20}$/,
+      inputErrorMessage: '鐢ㄦ埛瀵嗙爜闀垮害蹇呴』浠嬩簬 5 鍜� 20 涔嬮棿',
+      inputValidator: (value) => {
+        if (/<|>|"|'|\||\\/.test(value)) {
+          return '涓嶈兘鍖呭惈闈炴硶瀛楃锛�< > " \' \\\ |';
+        }
+      }
+    })
+  );
+  if (!err && res) {
     await api.resetUserPwd(row.userId, res.value);
-    proxy?.$modal.msgSuccess("淇敼鎴愬姛锛屾柊瀵嗙爜鏄細" + res.value);
+    proxy?.$modal.msgSuccess('淇敼鎴愬姛锛屾柊瀵嗙爜鏄細' + res.value);
   }
-}
+};
 
 /** 閫夋嫨鏉℃暟  */
 const handleSelectionChange = (selection: UserVO[]) => {
   ids.value = selection.map((item) => item.userId);
   single.value = selection.length != 1;
   multiple.value = !selection.length;
-}
+};
 
 /** 瀵煎叆鎸夐挳鎿嶄綔 */
 const handleImport = () => {
-  upload.title = "鐢ㄦ埛瀵煎叆";
+  upload.title = '鐢ㄦ埛瀵煎叆';
   upload.open = true;
-}
+};
 /** 瀵煎嚭鎸夐挳鎿嶄綔 */
 const handleExport = () => {
-  proxy?.download("system/user/export", {
-    ...queryParams.value,
-  }, `user_${new Date().getTime()}.xlsx`);
+  proxy?.download(
+    'system/user/export',
+    {
+      ...queryParams.value
+    },
+    `user_${new Date().getTime()}.xlsx`
+  );
 };
 /** 涓嬭浇妯℃澘鎿嶄綔 */
 const importTemplate = () => {
-  proxy?.download("system/user/importTemplate", {
-  }, `user_template_${new Date().getTime()}.xlsx`);
-}
+  proxy?.download('system/user/importTemplate', {}, `user_template_${new Date().getTime()}.xlsx`);
+};
 
 /**鏂囦欢涓婁紶涓鐞� */
 const handleFileUploadProgress = () => {
   upload.isUploading = true;
-}
+};
 /** 鏂囦欢涓婁紶鎴愬姛澶勭悊 */
 const handleFileSuccess = (response: any, file: UploadFile) => {
   upload.open = false;
   upload.isUploading = false;
   uploadRef.value?.handleRemove(file);
-  ElMessageBox.alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + "</div>", "瀵煎叆缁撴灉", { dangerouslyUseHTMLString: true });
+  ElMessageBox.alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + '</div>', '瀵煎叆缁撴灉', {
+    dangerouslyUseHTMLString: true
+  });
   getList();
-}
+};
 
 /** 鎻愪氦涓婁紶鏂囦欢 */
 function submitFileForm() {
@@ -538,59 +570,58 @@
     const { data } = await treeselect();
     deptOptions.value = data;
   }
-}
-
+};
 
 /** 閲嶇疆鎿嶄綔琛ㄥ崟 */
 const reset = () => {
   form.value = { ...initFormData };
   userFormRef.value?.resetFields();
-}
+};
 /** 鍙栨秷鎸夐挳 */
 const cancel = () => {
   dialog.visible = false;
   reset();
-}
+};
 
 /** 鏂板鎸夐挳鎿嶄綔 */
 const handleAdd = async () => {
   reset();
   const { data } = await api.getUser();
   dialog.visible = true;
-  dialog.title = "鏂板鐢ㄦ埛";
+  dialog.title = '鏂板鐢ㄦ埛';
   await initTreeData();
   postOptions.value = data.posts;
   roleOptions.value = data.roles;
   form.value.password = initPassword.value.toString();
-}
+};
+
 /** 淇敼鎸夐挳鎿嶄綔 */
 const handleUpdate = async (row?: UserForm) => {
   reset();
-  const userId = row?.userId || ids.value[0]
-  const { data } = await api.getUser(userId)
+  const userId = row?.userId || ids.value[0];
+  const { data } = await api.getUser(userId);
   dialog.visible = true;
-  dialog.title = "淇敼鐢ㄦ埛";
+  dialog.title = '淇敼鐢ㄦ埛';
   await initTreeData();
   Object.assign(form.value, data.user);
   postOptions.value = data.posts;
   roleOptions.value = data.roles;
   form.value.postIds = data.postIds;
   form.value.roleIds = data.roleIds;
-  form.value.password = "";
-}
+  form.value.password = '';
+};
 
 /** 鎻愪氦鎸夐挳 */
 const submitForm = () => {
   userFormRef.value?.validate(async (valid: boolean) => {
     if (valid) {
       form.value.userId ? await api.updateUser(form.value) : await api.addUser(form.value);
-      proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛");
+      proxy?.$modal.msgSuccess('鎿嶄綔鎴愬姛');
       dialog.visible = false;
       await getList();
     }
-  })
-}
-
+  });
+};
 
 /**
  * 鍏抽棴鐢ㄦ埛寮圭獥
@@ -598,7 +629,7 @@
 const closeDialog = () => {
   dialog.visible = false;
   resetForm();
-}
+};
 
 /**
  * 閲嶇疆琛ㄥ崟
@@ -609,14 +640,20 @@
 
   form.value.id = undefined;
   form.value.status = '1';
-}
+};
 onMounted(() => {
-  getTreeSelect() // 鍒濆鍖栭儴闂ㄦ暟鎹�
-  getList() // 鍒濆鍖栧垪琛ㄦ暟鎹�
-  proxy?.getConfigKey("sys.user.initPassword").then(response => {
+  getTreeSelect(); // 鍒濆鍖栭儴闂ㄦ暟鎹�
+  getList(); // 鍒濆鍖栧垪琛ㄦ暟鎹�
+  proxy?.getConfigKey('sys.user.initPassword').then((response) => {
     initPassword.value = response.data;
   });
 });
+
+async function handleDeptChange(value: number | string) {
+  const response = await optionselect(value);
+  postOptions.value = response.data;
+  form.value.postIds = [];
+}
 </script>
 
 <style lang="scss" scoped></style>
diff --git a/src/views/system/user/profile/index.vue b/src/views/system/user/profile/index.vue
index 47a6632..426fca8 100644
--- a/src/views/system/user/profile/index.vue
+++ b/src/views/system/user/profile/index.vue
@@ -3,14 +3,14 @@
     <el-row :gutter="20">
       <el-col :span="6" :xs="24">
         <el-card class="box-card">
-          <template v-slot:header>
+          <template #header>
             <div class="clearfix">
               <span>涓汉淇℃伅</span>
             </div>
           </template>
           <div>
             <div class="text-center">
-              <userAvatar/>
+              <userAvatar />
             </div>
             <ul class="list-group list-group-striped">
               <li class="list-group-item">
@@ -27,7 +27,7 @@
               </li>
               <li class="list-group-item">
                 <svg-icon icon-class="tree" />鎵�灞為儴闂�
-                <div class="pull-right" v-if="state.user.dept">{{ state.user.dept.deptName }} / {{ state.postGroup }}</div>
+                <div v-if="state.user.deptName" class="pull-right">{{ state.user.deptName }} / {{ state.postGroup }}</div>
               </li>
               <li class="list-group-item">
                 <svg-icon icon-class="peoples" />鎵�灞炶鑹�
@@ -43,7 +43,7 @@
       </el-col>
       <el-col :span="18" :xs="24">
         <el-card>
-          <template v-slot:header>
+          <template #header>
             <div class="clearfix">
               <span>鍩烘湰璧勬枡</span>
             </div>
@@ -58,6 +58,9 @@
             <el-tab-pane label="绗笁鏂瑰簲鐢�" name="thirdParty">
               <thirdParty :auths="state.auths" />
             </el-tab-pane>
+            <el-tab-pane label="鍦ㄧ嚎璁惧" name="onlinDevice">
+              <onlinDevice :devices="state.devices" />
+            </el-tab-pane>
           </el-tabs>
         </el-card>
       </el-col>
@@ -66,38 +69,54 @@
 </template>
 
 <script setup name="Profile" lang="ts">
-import UserAvatar from "./userAvatar.vue";
-import UserInfo from "./userInfo.vue";
-import ResetPwd from "./resetPwd.vue";
-import ThirdParty from "./thirdParty.vue";
-import { getAuthList } from "@/api/system/social/auth";
-import { getUserProfile } from "@/api/system/user";
+import UserAvatar from './userAvatar.vue';
+import UserInfo from './userInfo.vue';
+import ResetPwd from './resetPwd.vue';
+import ThirdParty from './thirdParty.vue';
+import OnlinDevice from './onlineDevice.vue';
+import { getAuthList } from '@/api/system/social/auth';
+import { getUserProfile } from '@/api/system/user';
+import { getOnline } from '@/api/monitor/online';
+import { UserVO } from '@/api/system/user/types';
 
-const activeTab = ref("userinfo");
-const state = ref<Record<string, any>>({
-    user: {},
-    roleGroup: '',
-    postGroup: '',
-    auths: []
+const activeTab = ref('userinfo');
+interface State {
+  user: Partial<UserVO>;
+  roleGroup: string;
+  postGroup: string;
+  auths: any;
+  devices: any;
+}
+const state = ref<State>({
+  user: {},
+  roleGroup: '',
+  postGroup: '',
+  auths: [],
+  devices: []
 });
 
 const userForm = ref({});
 
 const getUser = async () => {
-    const res = await getUserProfile();
-    state.value.user = res.data.user;
-    userForm.value = { ...res.data.user }
-    state.value.roleGroup = res.data.roleGroup;
-    state.value.postGroup = res.data.postGroup;
+  const res = await getUserProfile();
+  state.value.user = res.data.user;
+  userForm.value = { ...res.data.user };
+  state.value.roleGroup = res.data.roleGroup;
+  state.value.postGroup = res.data.postGroup;
 };
 
 const getAuths = async () => {
-    const res = await getAuthList();
-    state.value.auths = res.data;
+  const res = await getAuthList();
+  state.value.auths = res.data;
+};
+const getOnlines = async () => {
+  const res = await getOnline();
+  state.value.devices = res.rows;
 };
 
 onMounted(() => {
-    getUser();
-    getAuths();
-})
+  getUser();
+  getAuths();
+  getOnlines();
+});
 </script>
diff --git a/src/views/system/user/profile/onlineDevice.vue b/src/views/system/user/profile/onlineDevice.vue
new file mode 100644
index 0000000..8da96f6
--- /dev/null
+++ b/src/views/system/user/profile/onlineDevice.vue
@@ -0,0 +1,59 @@
+<template>
+  <div>
+    <el-table :data="devices" style="width: 100%; height: 100%; font-size: 14px">
+      <el-table-column label="璁惧绫诲瀷" align="center">
+        <template #default="scope">
+          <dict-tag :options="sys_device_type" :value="scope.row.deviceType" />
+        </template>
+      </el-table-column>
+      <el-table-column label="涓绘満" align="center" prop="ipaddr" :show-overflow-tooltip="true" />
+      <el-table-column label="鐧诲綍鍦扮偣" align="center" prop="loginLocation" :show-overflow-tooltip="true" />
+      <el-table-column label="鎿嶄綔绯荤粺" align="center" prop="os" :show-overflow-tooltip="true" />
+      <el-table-column label="娴忚鍣�" align="center" prop="browser" :show-overflow-tooltip="true" />
+      <el-table-column label="鐧诲綍鏃堕棿" align="center" prop="loginTime" width="180">
+        <template #default="scope">
+          <span>{{ parseTime(scope.row.loginTime) }}</span>
+        </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="Delete" @click="handldDelOnline(scope.row)">
+            </el-button>
+          </el-tooltip>
+        </template>
+      </el-table-column>
+    </el-table>
+  </div>
+</template>
+
+<script name="Online" lang="ts" setup>
+import { delOnline } from '@/api/monitor/online';
+import { propTypes } from '@/utils/propTypes';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { sys_device_type } = toRefs<any>(proxy?.useDict('sys_device_type'));
+
+const props = defineProps({
+  devices: propTypes.any.isRequired
+});
+const devices = computed(() => props.devices);
+
+/** 鍒犻櫎鎸夐挳鎿嶄綔 */
+const handldDelOnline = (row: any) => {
+  ElMessageBox.confirm('鍒犻櫎璁惧鍚庯紝鍦ㄨ璁惧鐧诲綍闇�瑕侀噸鏂拌繘琛岄獙璇�')
+    .then(() => {
+      return delOnline(row.tokenId);
+    })
+    .then((res: any) => {
+      if (res.code === 200) {
+        proxy?.$modal.msgSuccess('鍒犻櫎鎴愬姛');
+        proxy?.$tab.refreshPage();
+      } else {
+        proxy?.$modal.msgError(res.msg);
+      }
+    })
+    .catch(() => {});
+};
+
+</script>
diff --git a/src/views/system/user/profile/resetPwd.vue b/src/views/system/user/profile/resetPwd.vue
index 11b00f6..6d536da 100644
--- a/src/views/system/user/profile/resetPwd.vue
+++ b/src/views/system/user/profile/resetPwd.vue
@@ -17,37 +17,44 @@
 </template>
 
 <script setup lang="ts">
-import { updateUserPwd } from "@/api/system/user";
-import type { ResetPwdForm } from "@/api/system/user/types";
+import { updateUserPwd } from '@/api/system/user';
+import type { ResetPwdForm } from '@/api/system/user/types';
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 const pwdRef = ref<ElFormInstance>();
 const user = ref<ResetPwdForm>({
-  oldPassword: "",
-  newPassword: "",
-  confirmPassword: ""
+  oldPassword: '',
+  newPassword: '',
+  confirmPassword: ''
 });
 
 const equalToPassword = (rule: any, value: string, callback: any) => {
   if (user.value.newPassword !== value) {
-    callback(new Error("涓ゆ杈撳叆鐨勫瘑鐮佷笉涓�鑷�"));
+    callback(new Error('涓ゆ杈撳叆鐨勫瘑鐮佷笉涓�鑷�'));
   } else {
     callback();
   }
 };
 const rules = ref({
-  oldPassword: [{ required: true, message: "鏃у瘑鐮佷笉鑳戒负绌�", trigger: "blur" }],
-  newPassword: [{ required: true, message: "鏂板瘑鐮佷笉鑳戒负绌�", trigger: "blur" }, {
-    min: 6,
-    max: 20,
-    message: "闀垮害鍦� 6 鍒� 20 涓瓧绗�",
-    trigger: "blur"
-  }],
-  confirmPassword: [{ required: true, message: "纭瀵嗙爜涓嶈兘涓虹┖", trigger: "blur" }, {
-    required: true,
-    validator: equalToPassword,
-    trigger: "blur"
-  }]
+  oldPassword: [{ required: true, message: '鏃у瘑鐮佷笉鑳戒负绌�', trigger: 'blur' }],
+  newPassword: [
+    { required: true, message: '鏂板瘑鐮佷笉鑳戒负绌�', trigger: 'blur' },
+    {
+      min: 6,
+      max: 20,
+      message: '闀垮害鍦� 6 鍒� 20 涓瓧绗�',
+      trigger: 'blur'
+    },
+    { pattern: /^[^<>"'|\\]+$/, message: "涓嶈兘鍖呭惈闈炴硶瀛楃锛�< > \" ' \\\ |", trigger: "blur" }
+  ],
+  confirmPassword: [
+    { required: true, message: '纭瀵嗙爜涓嶈兘涓虹┖', trigger: 'blur' },
+    {
+      required: true,
+      validator: equalToPassword,
+      trigger: 'blur'
+    }
+  ]
 });
 
 /** 鎻愪氦鎸夐挳 */
@@ -55,7 +62,7 @@
   pwdRef.value?.validate(async (valid: boolean) => {
     if (valid) {
       await updateUserPwd(user.value.oldPassword, user.value.newPassword);
-      proxy?.$modal.msgSuccess("淇敼鎴愬姛");
+      proxy?.$modal.msgSuccess('淇敼鎴愬姛');
     }
   });
 };
diff --git a/src/views/system/user/profile/thirdParty.vue b/src/views/system/user/profile/thirdParty.vue
index 4170894..40daa86 100644
--- a/src/views/system/user/profile/thirdParty.vue
+++ b/src/views/system/user/profile/thirdParty.vue
@@ -1,17 +1,17 @@
 <template>
   <div>
-    <el-table :data="auths" style="width: 100%; height: 100%; font-size: 10px">
-      <el-table-column label="搴忓彿" width="50" type="index"></el-table-column>
+    <el-table :data="auths" style="width: 100%; height: 100%; font-size: 14px">
+      <el-table-column label="搴忓彿" width="50" type="index" />
       <el-table-column label="缁戝畾璐﹀彿骞冲彴" width="140" align="center" prop="source" show-overflow-tooltip />
       <el-table-column label="澶村儚" width="120" align="center" prop="avatar">
-        <template v-slot="scope">
+        <template #default="scope">
           <img :src="scope.row.avatar" style="width: 45px; height: 45px" />
         </template>
       </el-table-column>
       <el-table-column label="绯荤粺璐﹀彿" width="180" align="center" prop="userName" :show-overflow-tooltip="true" />
       <el-table-column label="缁戝畾鏃堕棿" width="180" align="center" prop="createTime" />
       <el-table-column label="鎿嶄綔" width="80" align="center" class-name="small-padding fixed-width">
-        <template v-slot="scope">
+        <template #default="scope">
           <el-button size="small" type="text" @click="unlockAuth(scope.row)">瑙g粦</el-button>
         </template>
       </el-table-column>
@@ -20,25 +20,31 @@
     <div id="git-user-binding">
       <h4 class="provider-desc">浣犲彲浠ョ粦瀹氫互涓嬬涓夋柟甯愬彿</h4>
       <div id="authlist" class="user-bind">
-        <a class="third-app" href="#" @click="authUrl('wechat');" title="浣跨敤 寰俊 璐﹀彿鎺堟潈鐧诲綍">
+        <a class="third-app" href="#" title="浣跨敤 寰俊 璐﹀彿鎺堟潈鐧诲綍" @click="authUrl('wechat')">
           <div class="git-other-login-icon">
             <svg-icon icon-class="wechat" />
           </div>
           <span class="app-name">WeiXin</span>
         </a>
-        <a class="third-app" href="#" @click="authUrl('maxkey');" title="浣跨敤 MaxKey 璐﹀彿鎺堟潈鐧诲綍">
+        <a class="third-app" href="#" title="浣跨敤 MaxKey 璐﹀彿鎺堟潈鐧诲綍" @click="authUrl('maxkey')">
           <div class="git-other-login-icon">
             <svg-icon icon-class="maxkey" />
           </div>
           <span class="app-name">MaxKey</span>
         </a>
-        <a class="third-app" href="#" @click="authUrl('gitee');" title="浣跨敤 Gitee 璐﹀彿鎺堟潈鐧诲綍">
+        <a class="third-app" href="#" title="浣跨敤 TopIam 璐﹀彿鎺堟潈鐧诲綍" @click="authUrl('topiam')">
+          <div class="git-other-login-icon">
+            <svg-icon icon-class="topiam" />
+          </div>
+          <span class="app-name">TopIam</span>
+        </a>
+        <a class="third-app" href="#" title="浣跨敤 Gitee 璐﹀彿鎺堟潈鐧诲綍" @click="authUrl('gitee')">
           <div class="git-other-login-icon">
             <svg-icon icon-class="gitee" />
           </div>
           <span class="app-name">Gitee</span>
         </a>
-        <a class="third-app" href="#" @click="authUrl('github');" title="浣跨敤 GitHub 璐﹀彿鎺堟潈鐧诲綍">
+        <a class="third-app" href="#" title="浣跨敤 GitHub 璐﹀彿鎺堟潈鐧诲綍" @click="authUrl('github')">
           <div class="git-other-login-icon">
             <svg-icon icon-class="github" />
           </div>
@@ -50,31 +56,30 @@
 </template>
 
 <script lang="ts" setup>
-import { authUnlock, authBinding } from "@/api/system/social/auth";
-import { PropType } from "vue";
+import { authUnlock, authBinding } from '@/api/system/social/auth';
+import { propTypes } from '@/utils/propTypes';
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
 const props = defineProps({
-  auths: {
-    type: Object as PropType<any>,
-  }
+  auths: propTypes.any.isRequired
 });
 const auths = computed(() => props.auths);
-
 
 const unlockAuth = (row: any) => {
   ElMessageBox.confirm('鎮ㄧ‘瀹氳瑙i櫎"' + row.source + '"鐨勮处鍙风粦瀹氬悧锛�')
     .then(() => {
       return authUnlock(row.id);
-    }).then((res: any) => {
+    })
+    .then((res: any) => {
       if (res.code === 200) {
-        proxy?.$modal.msgSuccess("瑙g粦鎴愬姛");
+        proxy?.$modal.msgSuccess('瑙g粦鎴愬姛');
         proxy?.$tab.refreshPage();
       } else {
         proxy?.$modal.msgError(res.msg);
       }
-    }).catch(() => { });
+    })
+    .catch(() => {});
 };
 
 const authUrl = (source: string) => {
@@ -111,7 +116,7 @@
   margin-top: 10px;
 }
 
-.git-other-login-icon>img {
+.git-other-login-icon > img {
   height: 32px;
 }
 
@@ -122,15 +127,13 @@
 }
 
 .provider-desc {
-  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial,
-    "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Liberation Sans",
-    "PingFang SC", "Microsoft YaHei", "Hiragino Sans GB", "Wenquanyi Micro Hei",
-    "WenQuanYi Zen Hei", "ST Heiti", SimHei, SimSun, "WenQuanYi Zen Hei Sharp",
-    sans-serif;
+  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
+    'Liberation Sans', 'PingFang SC', 'Microsoft YaHei', 'Hiragino Sans GB', 'Wenquanyi Micro Hei', 'WenQuanYi Zen Hei', 'ST Heiti', SimHei, SimSun,
+    'WenQuanYi Zen Hei Sharp', sans-serif;
   font-size: 1.071rem;
 }
 
-td>img {
+td > img {
   height: 20px;
   width: 20px;
   display: inline-block;
diff --git a/src/views/system/user/profile/userAvatar.vue b/src/views/system/user/profile/userAvatar.vue
index ce25c11..32b6f5c 100644
--- a/src/views/system/user/profile/userAvatar.vue
+++ b/src/views/system/user/profile/userAvatar.vue
@@ -1,20 +1,20 @@
 <template>
   <div class="user-info-head" @click="editCropper()">
     <img :src="options.img" title="鐐瑰嚮涓婁紶澶村儚" class="img-circle img-lg" />
-    <el-dialog :title="title" v-model="open" width="800px" append-to-body @opened="modalOpened" @close="closeDialog">
+    <el-dialog v-model="open" :title="title" width="800px" append-to-body @opened="modalOpened" @close="closeDialog">
       <el-row>
         <el-col :xs="24" :md="12" :style="{ height: '350px' }">
           <vue-cropper
+            v-if="visible"
             ref="cropper"
             :img="options.img"
             :info="true"
-            :autoCrop="options.autoCrop"
-            :autoCropWidth="options.autoCropWidth"
-            :autoCropHeight="options.autoCropHeight"
-            :fixedBox="options.fixedBox"
-            :outputType="options.outputType"
-            @realTime="realTime"
-            v-if="visible"
+            :auto-crop="options.autoCrop"
+            :auto-crop-width="options.autoCropWidth"
+            :auto-crop-height="options.autoCropHeight"
+            :fixed-box="options.fixedBox"
+            :output-type="options.outputType"
+            @real-time="realTime"
           />
         </el-col>
         <el-col :xs="24" :md="12" :style="{ height: '350px' }">
@@ -56,10 +56,11 @@
 </template>
 
 <script setup lang="ts">
-import "vue-cropper/dist/index.css";
-import { VueCropper } from "vue-cropper";
-import { uploadAvatar } from "@/api/system/user";
-import useUserStore from "@/store/modules/user";
+import 'vue-cropper/dist/index.css';
+import { VueCropper } from 'vue-cropper';
+import { uploadAvatar } from '@/api/system/user';
+import useUserStore from '@/store/modules/user';
+import { UploadRawFile } from 'element-plus';
 
 interface Options {
   img: string | any; // 瑁佸壀鍥剧墖鐨勫湴鍧�
@@ -73,13 +74,12 @@
   visible: boolean;
 }
 
-
 const userStore = useUserStore();
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
 const open = ref(false);
 const visible = ref(false);
-const title = ref("淇敼澶村儚");
+const title = ref('淇敼澶村儚');
 
 const cropper = ref<any>({});
 //鍥剧墖瑁佸壀鏁版嵁
@@ -89,8 +89,8 @@
   autoCropWidth: 200,
   autoCropHeight: 200,
   fixedBox: true,
-  outputType: "png",
-  fileName: "",
+  outputType: 'png',
+  fileName: '',
   previews: {},
   visible: false
 });
@@ -104,8 +104,7 @@
   visible.value = true;
 };
 /** 瑕嗙洊榛樿涓婁紶琛屼负 */
-const requestUpload = (): any => {
-};
+const requestUpload = (): any => {};
 /** 鍚戝乏鏃嬭浆 */
 const rotateLeft = () => {
   cropper.value.rotateLeft();
@@ -120,9 +119,9 @@
   cropper.value.changeScale(num);
 };
 /** 涓婁紶棰勫鐞� */
-const beforeUpload = (file: any) => {
-  if (file.type.indexOf("image/") == -1) {
-    proxy?.$modal.msgError("鏂囦欢鏍煎紡閿欒锛岃涓婁紶鍥剧墖绫诲瀷,濡傦細JPG锛孭NG鍚庣紑鐨勬枃浠躲��");
+const beforeUpload = (file: UploadRawFile): any => {
+  if (file.type.indexOf('image/') == -1) {
+    proxy?.$modal.msgError('鏂囦欢鏍煎紡閿欒锛岃涓婁紶鍥剧墖绫诲瀷,濡傦細JPG锛孭NG鍚庣紑鐨勬枃浠躲��');
   } else {
     const reader = new FileReader();
     reader.readAsDataURL(file);
@@ -136,12 +135,12 @@
 const uploadImg = async () => {
   cropper.value.getCropBlob(async (data: any) => {
     let formData = new FormData();
-    formData.append("avatarfile", data, options.fileName);
+    formData.append('avatarfile', data, options.fileName);
     const res = await uploadAvatar(formData);
     open.value = false;
     options.img = res.data.imgUrl;
-    userStore.setAvatar(options.img as string)
-    proxy?.$modal.msgSuccess("淇敼鎴愬姛");
+    userStore.setAvatar(options.img);
+    proxy?.$modal.msgSuccess('淇敼鎴愬姛');
     visible.value = false;
   });
 };
@@ -164,7 +163,7 @@
 }
 
 .user-info-head:hover:after {
-  content: "+";
+  content: '+';
   position: absolute;
   left: 0;
   right: 0;
diff --git a/src/views/system/user/profile/userInfo.vue b/src/views/system/user/profile/userInfo.vue
index 3dd5ff7..d5b8a4c 100644
--- a/src/views/system/user/profile/userInfo.vue
+++ b/src/views/system/user/profile/userInfo.vue
@@ -11,8 +11,8 @@
     </el-form-item>
     <el-form-item label="鎬у埆">
       <el-radio-group v-model="userForm.sex">
-        <el-radio label="0">鐢�</el-radio>
-        <el-radio label="1">濂�</el-radio>
+        <el-radio value="0">鐢�</el-radio>
+        <el-radio value="1">濂�</el-radio>
       </el-radio-group>
     </el-form-item>
     <el-form-item>
@@ -23,38 +23,42 @@
 </template>
 
 <script setup lang="ts">
-import { updateUserProfile } from "@/api/system/user";
+import { updateUserProfile } from '@/api/system/user';
+import { propTypes } from '@/utils/propTypes';
 
 const props = defineProps({
-  user: {
-    type: Object as PropType<any>,
-    required: true
-  }
+  user: propTypes.any.isRequired
 });
 const userForm = computed(() => props.user);
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 const userRef = ref<ElFormInstance>();
-const rules = ref<ElFormRules>({
-  nickName: [{ required: true, message: "鐢ㄦ埛鏄电О涓嶈兘涓虹┖", trigger: "blur" }],
-  email: [{ required: true, message: "閭鍦板潃涓嶈兘涓虹┖", trigger: "blur" }, {
-    type: "email",
-    message: "璇疯緭鍏ユ纭殑閭鍦板潃",
-    trigger: ["blur", "change"]
-  }],
-  phonenumber: [{
-    required: true,
-    message: "鎵嬫満鍙风爜涓嶈兘涓虹┖",
-    trigger: "blur"
-  }, { pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "璇疯緭鍏ユ纭殑鎵嬫満鍙风爜", trigger: "blur" }]
-});
-
+const rule: ElFormRules = {
+  nickName: [{ required: true, message: '鐢ㄦ埛鏄电О涓嶈兘涓虹┖', trigger: 'blur' }],
+  email: [
+    { required: true, message: '閭鍦板潃涓嶈兘涓虹┖', trigger: 'blur' },
+    {
+      type: 'email',
+      message: '璇疯緭鍏ユ纭殑閭鍦板潃',
+      trigger: ['blur', 'change']
+    }
+  ],
+  phonenumber: [
+    {
+      required: true,
+      message: '鎵嬫満鍙风爜涓嶈兘涓虹┖',
+      trigger: 'blur'
+    },
+    { pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: '璇疯緭鍏ユ纭殑鎵嬫満鍙风爜', trigger: 'blur' }
+  ]
+};
+const rules = ref<ElFormRules>(rule);
 
 /** 鎻愪氦鎸夐挳 */
 const submit = () => {
   userRef.value?.validate(async (valid: boolean) => {
     if (valid) {
       await updateUserProfile(props.user);
-      proxy?.$modal.msgSuccess("淇敼鎴愬姛");
+      proxy?.$modal.msgSuccess('淇敼鎴愬姛');
     }
   });
 };
diff --git a/src/views/tool/gen/basicInfoForm.vue b/src/views/tool/gen/basicInfoForm.vue
index c77d0c9..5412088 100644
--- a/src/views/tool/gen/basicInfoForm.vue
+++ b/src/views/tool/gen/basicInfoForm.vue
@@ -3,27 +3,27 @@
     <el-row>
       <el-col :span="12">
         <el-form-item label="琛ㄥ悕绉�" prop="tableName">
-          <el-input placeholder="璇疯緭鍏ヤ粨搴撳悕绉�" v-model="infoForm.tableName" />
+          <el-input v-model="infoForm.tableName" placeholder="璇疯緭鍏ヤ粨搴撳悕绉�" />
         </el-form-item>
       </el-col>
       <el-col :span="12">
         <el-form-item label="琛ㄦ弿杩�" prop="tableComment">
-          <el-input placeholder="璇疯緭鍏�" v-model="infoForm.tableComment" />
+          <el-input v-model="infoForm.tableComment" placeholder="璇疯緭鍏�" />
         </el-form-item>
       </el-col>
       <el-col :span="12">
         <el-form-item label="瀹炰綋绫诲悕绉�" prop="className">
-          <el-input placeholder="璇疯緭鍏�" v-model="infoForm.className" />
+          <el-input v-model="infoForm.className" placeholder="璇疯緭鍏�" />
         </el-form-item>
       </el-col>
       <el-col :span="12">
         <el-form-item label="浣滆��" prop="functionAuthor">
-          <el-input placeholder="璇疯緭鍏�" v-model="infoForm.functionAuthor" />
+          <el-input v-model="infoForm.functionAuthor" placeholder="璇疯緭鍏�" />
         </el-form-item>
       </el-col>
       <el-col :span="24">
         <el-form-item label="澶囨敞" prop="remark">
-          <el-input type="textarea" :rows="3" v-model="infoForm.remark"></el-input>
+          <el-input v-model="infoForm.remark" type="textarea" :rows="3"></el-input>
         </el-form-item>
       </el-col>
     </el-row>
@@ -31,19 +31,19 @@
 </template>
 
 <script setup lang="ts">
-import { propTypes } from "@/utils/propTypes";
+import { propTypes } from '@/utils/propTypes';
 
 const prop = defineProps({
   info: propTypes.any.def({})
 });
 
-const infoForm = computed(() => prop.info)
+const infoForm = computed(() => prop.info);
 
 // 琛ㄥ崟鏍¢獙
 const rules = ref({
-  tableName: [{ required: true, message: "璇疯緭鍏ヨ〃鍚嶇О", trigger: "blur" }],
-  tableComment: [{ required: true, message: "璇疯緭鍏ヨ〃鎻忚堪", trigger: "blur" }],
-  className: [{ required: true, message: "璇疯緭鍏ュ疄浣撶被鍚嶇О", trigger: "blur" }],
-  functionAuthor: [{ required: true, message: "璇疯緭鍏ヤ綔鑰�", trigger: "blur" }]
+  tableName: [{ required: true, message: '璇疯緭鍏ヨ〃鍚嶇О', trigger: 'blur' }],
+  tableComment: [{ required: true, message: '璇疯緭鍏ヨ〃鎻忚堪', trigger: 'blur' }],
+  className: [{ required: true, message: '璇疯緭鍏ュ疄浣撶被鍚嶇О', trigger: 'blur' }],
+  functionAuthor: [{ required: true, message: '璇疯緭鍏ヤ綔鑰�', trigger: 'blur' }]
 });
 </script>
diff --git a/src/views/tool/gen/editTable.vue b/src/views/tool/gen/editTable.vue
index fdd1eeb..bbb0889 100644
--- a/src/views/tool/gen/editTable.vue
+++ b/src/views/tool/gen/editTable.vue
@@ -35,22 +35,22 @@
 
           <el-table-column label="鎻掑叆" min-width="5%">
             <template #default="scope">
-              <el-checkbox true-label="1" false-label="0" v-model="scope.row.isInsert"></el-checkbox>
+              <el-checkbox v-model="scope.row.isInsert" true-value="1" false-value="0"></el-checkbox>
             </template>
           </el-table-column>
           <el-table-column label="缂栬緫" min-width="5%">
             <template #default="scope">
-              <el-checkbox true-label="1" false-label="0" v-model="scope.row.isEdit"></el-checkbox>
+              <el-checkbox v-model="scope.row.isEdit" true-value="1" false-value="0"></el-checkbox>
             </template>
           </el-table-column>
           <el-table-column label="鍒楄〃" min-width="5%">
             <template #default="scope">
-              <el-checkbox true-label="1" false-label="0" v-model="scope.row.isList"></el-checkbox>
+              <el-checkbox v-model="scope.row.isList" true-value="1" false-value="0"></el-checkbox>
             </template>
           </el-table-column>
           <el-table-column label="鏌ヨ" min-width="5%">
             <template #default="scope">
-              <el-checkbox true-label="1" false-label="0" v-model="scope.row.isQuery"></el-checkbox>
+              <el-checkbox v-model="scope.row.isQuery" true-value="1" false-value="0"></el-checkbox>
             </template>
           </el-table-column>
           <el-table-column label="鏌ヨ鏂瑰紡" min-width="10%">
@@ -69,7 +69,7 @@
           </el-table-column>
           <el-table-column label="蹇呭~" min-width="5%">
             <template #default="scope">
-              <el-checkbox true-label="1" false-label="0" v-model="scope.row.isRequired"></el-checkbox>
+              <el-checkbox v-model="scope.row.isRequired" true-value="1" false-value="0"></el-checkbox>
             </template>
           </el-table-column>
           <el-table-column label="鏄剧ず绫诲瀷" min-width="12%">
@@ -104,7 +104,7 @@
       </el-tab-pane>
     </el-tabs>
     <el-form label-width="100px">
-      <div style="text-align: center;margin-left:-100px;margin-top:10px;">
+      <div style="text-align: center; margin-left: -100px; margin-top: 10px">
         <el-button type="primary" @click="submitForm()">鎻愪氦</el-button>
         <el-button @click="close()">杩斿洖</el-button>
       </div>
@@ -118,7 +118,8 @@
 import { optionselect as getDictOptionselect } from '@/api/system/dict/type';
 import { DictTypeVO } from '@/api/system/dict/type/types';
 import BasicInfoForm from './basicInfoForm.vue';
-import GenInfoForm from "./genInfoForm.vue";
+import GenInfoForm from './genInfoForm.vue';
+import { RouteLocationNormalized } from 'vue-router';
 
 const route = useRoute();
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@@ -138,8 +139,8 @@
   const basicForm = basicInfo.value?.$refs.basicInfoForm;
   const genForm = genInfo.value?.$refs.genInfoForm;
 
-  Promise.all([basicForm, genForm].map(getFormPromise)).then(async res => {
-    const validateResult = res.every(item => !!item);
+  Promise.all([basicForm, genForm].map(getFormPromise)).then(async (res) => {
+    const validateResult = res.every((item) => !!item);
     if (validateResult) {
       const genTable: any = Object.assign({}, info.value);
       genTable.columns = columns.value;
@@ -155,24 +156,34 @@
         close();
       }
     } else {
-      proxy?.$modal.msgError("琛ㄥ崟鏍¢獙鏈�氳繃锛岃閲嶆柊妫�鏌ユ彁浜ゅ唴瀹�");
+      proxy?.$modal.msgError('琛ㄥ崟鏍¢獙鏈�氳繃锛岃閲嶆柊妫�鏌ユ彁浜ゅ唴瀹�');
     }
   });
-}
+};
 const getFormPromise = (form: any) => {
-  return new Promise(resolve => {
+  return new Promise((resolve) => {
     form.validate((res: any) => {
       resolve(res);
     });
   });
-}
+};
 const close = () => {
-  const obj = { path: "/tool/gen", query: { t: Date.now(), pageNum: route.query.pageNum } };
+  const obj: RouteLocationNormalized = {
+    path: '/tool/gen',
+    fullPath: '',
+    hash: '',
+    matched: [],
+    meta: undefined,
+    name: undefined,
+    params: undefined,
+    redirectedFrom: undefined,
+    query: { t: Date.now().toString(), pageNum: route.query.pageNum }
+  };
   proxy?.$tab.closeOpenPage(obj);
-}
+};
 
 (async () => {
-  const tableId = route.params && route.params.tableId as string;
+  const tableId = route.params && (route.params.tableId as string);
   if (tableId) {
     // 鑾峰彇琛ㄨ缁嗕俊鎭�
     const res = await getGenTable(tableId);
diff --git a/src/views/tool/gen/genInfoForm.vue b/src/views/tool/gen/genInfoForm.vue
index 94486e2..0ee9f1d 100644
--- a/src/views/tool/gen/genInfoForm.vue
+++ b/src/views/tool/gen/genInfoForm.vue
@@ -90,12 +90,12 @@
               <el-icon><question-filled /></el-icon>
             </el-tooltip>
           </template>
-          <el-radio v-model="infoForm.genType" label="0">zip鍘嬬缉鍖�</el-radio>
-          <el-radio v-model="infoForm.genType" label="1">鑷畾涔夎矾寰�</el-radio>
+          <el-radio v-model="infoForm.genType" value="0">zip鍘嬬缉鍖�</el-radio>
+          <el-radio v-model="infoForm.genType" value="1">鑷畾涔夎矾寰�</el-radio>
         </el-form-item>
       </el-col>
 
-      <el-col :span="24" v-if="infoForm.genType == '1'">
+      <el-col v-if="infoForm.genType == '1'" :span="24">
         <el-form-item prop="genPath">
           <template #label>
             鑷畾涔夎矾寰�
@@ -223,21 +223,21 @@
 
 <script setup lang="ts">
 import { listMenu } from '@/api/system/menu';
-import { propTypes } from "@/utils/propTypes";
+import { propTypes } from '@/utils/propTypes';
 
 interface MenuOptionsType {
   menuId: number | string;
   menuName: string;
-  children: MenuOptionsType[] | undefined;
+  children?: MenuOptionsType[];
 }
+const { proxy } = getCurrentInstance();
 
 const subColumns = ref<any>([]);
 const menuOptions = ref<Array<MenuOptionsType>>([]);
-const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 
 const props = defineProps({
-  info: propTypes.any.def(null),
-  tables: propTypes.any.def(null)
+  info: propTypes.any.isRequired,
+  tables: propTypes.any.isRequired
 });
 
 const infoForm = computed(() => props.info);
@@ -246,21 +246,21 @@
 
 // 琛ㄥ崟鏍¢獙
 const rules = ref({
-  tplCategory: [{ required: true, message: "璇烽�夋嫨鐢熸垚妯℃澘", trigger: "blur" }],
-  packageName: [{ required: true, message: "璇疯緭鍏ョ敓鎴愬寘璺緞", trigger: "blur" }],
-  moduleName: [{ required: true, message: "璇疯緭鍏ョ敓鎴愭ā鍧楀悕", trigger: "blur" }],
-  businessName: [{ required: true, message: "璇疯緭鍏ョ敓鎴愪笟鍔″悕", trigger: "blur" }],
-  functionName: [{ required: true, message: "璇疯緭鍏ョ敓鎴愬姛鑳藉悕", trigger: "blur" }]
+  tplCategory: [{ required: true, message: '璇烽�夋嫨鐢熸垚妯℃澘', trigger: 'blur' }],
+  packageName: [{ required: true, message: '璇疯緭鍏ョ敓鎴愬寘璺緞', trigger: 'blur' }],
+  moduleName: [{ required: true, message: '璇疯緭鍏ョ敓鎴愭ā鍧楀悕', trigger: 'blur' }],
+  businessName: [{ required: true, message: '璇疯緭鍏ョ敓鎴愪笟鍔″悕', trigger: 'blur' }],
+  functionName: [{ required: true, message: '璇疯緭鍏ョ敓鎴愬姛鑳藉悕', trigger: 'blur' }]
 });
 const subSelectChange = () => {
-  infoForm.value.subTableFkName = "";
-}
+  infoForm.value.subTableFkName = '';
+};
 const tplSelectChange = (value: string) => {
-  if (value !== "sub") {
-    infoForm.value.subTableName = "";
-    infoForm.value.subTableFkName = "";
+  if (value !== 'sub') {
+    infoForm.value.subTableName = '';
+    infoForm.value.subTableFkName = '';
   }
-}
+};
 const setSubTableColumns = (value: string) => {
   table.value.forEach((item: any) => {
     const name = item.tableName;
@@ -268,24 +268,28 @@
       subColumns.value = item.columns;
       return;
     }
-  })
-}
+  });
+};
 
 /** 鏌ヨ鑿滃崟涓嬫媺鏍戠粨鏋� */
 const getMenuTreeselect = async () => {
   const res = await listMenu();
-  res.data.forEach(m => m.menuId = m.menuId.toString());
-  const data = proxy?.handleTree<MenuOptionsType>(res.data, "menuId");
-  if (data) {
-    menuOptions.value = data
-  }
-}
+  res.data.forEach((m) => (m.menuId = m.menuId.toString()));
+  const data = proxy?.handleTree<MenuOptionsType>(res.data, 'menuId');
 
-watch(() => props.info.subTableName, val => {
-  setSubTableColumns(val);
-});
+  if (data) {
+    menuOptions.value = data;
+  }
+};
+
+watch(
+  () => props.info.subTableName,
+  (val) => {
+    setSubTableColumns(val);
+  }
+);
 
 onMounted(() => {
   getMenuTreeselect();
-})
+});
 </script>
diff --git a/src/views/tool/gen/importTable.vue b/src/views/tool/gen/importTable.vue
index 1ba79ec..50ec0ee 100644
--- a/src/views/tool/gen/importTable.vue
+++ b/src/views/tool/gen/importTable.vue
@@ -1,9 +1,9 @@
 <template>
   <!-- 瀵煎叆琛� -->
-  <el-dialog title="瀵煎叆琛�" v-model="visible" width="1100px" top="5vh" append-to-body>
-    <el-form :model="queryParams" ref="queryFormRef" :inline="true">
+  <el-dialog v-model="visible" title="瀵煎叆琛�" width="1100px" top="5vh" append-to-body>
+    <el-form ref="queryFormRef" :model="queryParams" :inline="true">
       <el-form-item label="鏁版嵁婧�" prop="dataName">
-        <el-select v-model="queryParams.dataName" filterable placeholder="璇烽�夋嫨/杈撳叆鏁版嵁婧愬悕绉�" style="width: 200px">
+        <el-select v-model="queryParams.dataName" filterable placeholder="璇烽�夋嫨/杈撳叆鏁版嵁婧愬悕绉�">
           <el-option v-for="item in dataNameList" :key="item" :label="item" :value="item"> </el-option>
         </el-select>
       </el-form-item>
@@ -19,14 +19,14 @@
       </el-form-item>
     </el-form>
     <el-row>
-      <el-table @row-click="clickRow" ref="tableRef" :data="dbTableList" @selection-change="handleSelectionChange" height="260px">
+      <el-table ref="tableRef" :data="dbTableList" height="260px" @row-click="clickRow" @selection-change="handleSelectionChange">
         <el-table-column type="selection" width="55"></el-table-column>
         <el-table-column prop="tableName" label="琛ㄥ悕绉�" :show-overflow-tooltip="true"></el-table-column>
         <el-table-column prop="tableComment" label="琛ㄦ弿杩�" :show-overflow-tooltip="true"></el-table-column>
         <el-table-column prop="createTime" label="鍒涘缓鏃堕棿"></el-table-column>
         <el-table-column prop="updateTime" label="鏇存柊鏃堕棿"></el-table-column>
       </el-table>
-      <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
+      <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
     </el-row>
     <template #footer>
       <div class="dialog-footer">
@@ -59,7 +59,7 @@
 });
 const dataNameList = ref<Array<string>>([]);
 
-const emit = defineEmits(["ok"]);
+const emit = defineEmits(['ok']);
 
 /** 鏌ヨ鍙傛暟鍒楄〃 */
 const show = (dataName: string) => {
@@ -71,53 +71,53 @@
   }
   getList();
   visible.value = true;
-}
+};
 /** 鍗曞嚮閫夋嫨琛� */
 const clickRow = (row: DbTableVO) => {
   // ele bug
   tableRef.value?.toggleRowSelection(row, false);
-}
+};
 /** 澶氶�夋閫変腑鏁版嵁 */
 const handleSelectionChange = (selection: DbTableVO[]) => {
-  tables.value = selection.map(item => item.tableName);
-}
+  tables.value = selection.map((item) => item.tableName);
+};
 /** 鏌ヨ琛ㄦ暟鎹� */
 const getList = async () => {
   const res = await listDbTable(queryParams);
   dbTableList.value = res.rows;
   total.value = res.total;
-}
+};
 /** 鎼滅储鎸夐挳鎿嶄綔 */
 const handleQuery = () => {
   queryParams.pageNum = 1;
   getList();
-}
+};
 /** 閲嶇疆鎸夐挳鎿嶄綔 */
 const resetQuery = () => {
   queryFormRef.value?.resetFields();
   handleQuery();
-}
+};
 /** 瀵煎叆鎸夐挳鎿嶄綔 */
 const handleImportTable = async () => {
-  const tableNames = tables.value.join(",");
-  if (tableNames == "") {
-    proxy?.$modal.msgError("璇烽�夋嫨瑕佸鍏ョ殑琛�");
+  const tableNames = tables.value.join(',');
+  if (tableNames == '') {
+    proxy?.$modal.msgError('璇烽�夋嫨瑕佸鍏ョ殑琛�');
     return;
   }
   const res = await importTable({ tables: tableNames, dataName: queryParams.dataName });
   proxy?.$modal.msgSuccess(res.msg);
   if (res.code === 200) {
     visible.value = false;
-    emit("ok");
+    emit('ok');
   }
-}
+};
 /** 鏌ヨ澶氭暟鎹簮鍚嶇О */
 const getDataNameList = async () => {
-  const res = await getDataNames()
+  const res = await getDataNames();
   dataNameList.value = res.data;
-}
+};
 
 defineExpose({
-  show,
+  show
 });
 </script>
diff --git a/src/views/tool/gen/index.vue b/src/views/tool/gen/index.vue
index cb684f8..2618a20 100644
--- a/src/views/tool/gen/index.vue
+++ b/src/views/tool/gen/index.vue
@@ -1,20 +1,20 @@
 <template>
   <div class="p-2">
     <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
-      <div class="mb-[10px]" v-show="showSearch">
+      <div v-show="showSearch" class="mb-[10px]">
         <el-card shadow="hover">
-          <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
+          <el-form ref="queryFormRef" :model="queryParams" :inline="true">
             <el-form-item label="鏁版嵁婧�" prop="dataName">
-              <el-select v-model="queryParams.dataName" filterable clearable placeholder="璇烽�夋嫨/杈撳叆鏁版嵁婧愬悕绉�" style="width: 200px">
+              <el-select v-model="queryParams.dataName" filterable clearable placeholder="璇烽�夋嫨/杈撳叆鏁版嵁婧愬悕绉�">
                 <el-option key="" label="鍏ㄩ儴" value="" />
                 <el-option v-for="item in dataNameList" :key="item" :label="item" :value="item"> </el-option>
               </el-select>
             </el-form-item>
             <el-form-item label="琛ㄥ悕绉�" prop="tableName">
-              <el-input v-model="queryParams.tableName" placeholder="璇疯緭鍏ヨ〃鍚嶇О" clearable style="width: 200px" @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.tableName" placeholder="璇疯緭鍏ヨ〃鍚嶇О" clearable @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item label="琛ㄦ弿杩�" prop="tableComment">
-              <el-input v-model="queryParams.tableComment" placeholder="璇疯緭鍏ヨ〃鎻忚堪" clearable style="width: 200px" @keyup.enter="handleQuery" />
+              <el-input v-model="queryParams.tableComment" placeholder="璇疯緭鍏ヨ〃鎻忚堪" clearable @keyup.enter="handleQuery" />
             </el-form-item>
             <el-form-item label="鍒涘缓鏃堕棿" style="width: 308px">
               <el-date-picker
@@ -39,20 +39,20 @@
       <template #header>
         <el-row :gutter="10" class="mb8">
           <el-col :span="1.5">
-            <el-button type="primary" plain icon="Download" @click="handleGenTable()" v-hasPermi="['tool:gen:code']">鐢熸垚</el-button>
+            <el-button v-hasPermi="['tool:gen:code']" type="primary" plain icon="Download" @click="handleGenTable()">鐢熸垚</el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="info" plain icon="Upload" @click="openImportTable" v-hasPermi="['tool:gen:import']">瀵煎叆</el-button>
+            <el-button v-hasPermi="['tool:gen:import']" type="info" plain icon="Upload" @click="openImportTable">瀵煎叆</el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="success" plain icon="Edit" :disabled="single" @click="handleEditTable()" v-hasPermi="['tool:gen:edit']">淇敼</el-button>
+            <el-button v-hasPermi="['tool:gen:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleEditTable()">淇敼</el-button>
           </el-col>
           <el-col :span="1.5">
-            <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['tool:gen:remove']">
+            <el-button v-hasPermi="['tool:gen:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">
               鍒犻櫎
             </el-button>
           </el-col>
-          <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
+          <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
         </el-row>
       </template>
 
@@ -72,36 +72,36 @@
         <el-table-column label="鎿嶄綔" align="center" width="330" class-name="small-padding fixed-width">
           <template #default="scope">
             <el-tooltip content="棰勮" placement="top">
-              <el-button link type="primary" icon="View" @click="handlePreview(scope.row)" v-hasPermi="['tool:gen:preview']"></el-button>
+              <el-button v-hasPermi="['tool:gen:preview']" link type="primary" icon="View" @click="handlePreview(scope.row)"></el-button>
             </el-tooltip>
             <el-tooltip content="缂栬緫" placement="top">
-              <el-button link type="primary" icon="Edit" @click="handleEditTable(scope.row)" v-hasPermi="['tool:gen:edit']"></el-button>
+              <el-button v-hasPermi="['tool:gen:edit']" link type="primary" icon="Edit" @click="handleEditTable(scope.row)"></el-button>
             </el-tooltip>
             <el-tooltip content="鍒犻櫎" placement="top">
-              <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['tool:gen:remove']"></el-button>
+              <el-button v-hasPermi="['tool:gen:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
             </el-tooltip>
             <el-tooltip content="鍚屾" placement="top">
-              <el-button link type="primary" icon="Refresh" @click="handleSynchDb(scope.row)" v-hasPermi="['tool:gen:edit']"></el-button>
+              <el-button v-hasPermi="['tool:gen:edit']" link type="primary" icon="Refresh" @click="handleSynchDb(scope.row)"></el-button>
             </el-tooltip>
             <el-tooltip content="鐢熸垚浠g爜" placement="top">
-              <el-button link type="primary" icon="Download" @click="handleGenTable(scope.row)" v-hasPermi="['tool:gen:code']"></el-button>
+              <el-button v-hasPermi="['tool:gen:code']" link type="primary" icon="Download" @click="handleGenTable(scope.row)"></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" />
+      <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
     </el-card>
 
     <!-- 棰勮鐣岄潰 -->
-    <el-dialog :title="dialog.title" v-model="dialog.visible" width="80%" top="5vh" append-to-body class="scrollbar">
+    <el-dialog v-model="dialog.visible" :title="dialog.title" width="80%" top="5vh" append-to-body class="scrollbar">
       <el-tabs v-model="preview.activeName">
         <el-tab-pane
           v-for="(value, key) in preview.data"
+          :key="value"
           :label="key.substring(key.lastIndexOf('/') + 1, key.indexOf('.vm'))"
           :name="key.substring(key.lastIndexOf('/') + 1, key.indexOf('.vm'))"
-          :key="value"
         >
-          <el-link :underline="false" icon="DocumentCopy" v-copyText="value" v-copyText:callback="copyTextSuccess" style="float:right">
+          <el-link v-copyText="value" v-copyText:callback="copyTextSuccess" :underline="false" icon="DocumentCopy" style="float: right">
             &nbsp;澶嶅埗
           </el-link>
           <pre>{{ value }}</pre>
@@ -129,7 +129,7 @@
 const multiple = ref(true);
 const total = ref(0);
 const dateRange = ref<[DateModelType, DateModelType]>(['', '']);
-const uniqueId = ref("");
+const uniqueId = ref('');
 const dataNameList = ref<Array<string>>([]);
 
 const queryFormRef = ref<ElFormInstance>();
@@ -140,13 +140,16 @@
   pageSize: 10,
   tableName: '',
   tableComment: '',
-  dataName: ""
-})
+  dataName: ''
+});
 
-const preview = ref<any>({
+const preview = ref<{
+  data: Record<string, string>;
+  activeName: string;
+}>({
   data: {},
   activeName: 'domain.java'
-})
+});
 const dialog = reactive<DialogOption>({
   visible: false,
   title: '浠g爜棰勮'
@@ -161,13 +164,13 @@
     queryFormRef.value?.resetFields();
     getList();
   }
-})
+});
 
 /** 鏌ヨ澶氭暟鎹簮鍚嶇О */
 const getDataNameList = async () => {
-  const res = await getDataNames()
+  const res = await getDataNames();
   dataNameList.value = res.data;
-}
+};
 
 /** 鏌ヨ琛ㄩ泦鍚� */
 const getList = async () => {
@@ -176,65 +179,65 @@
   tableList.value = res.rows;
   total.value = res.total;
   loading.value = false;
-}
+};
 /** 鎼滅储鎸夐挳鎿嶄綔 */
 const handleQuery = () => {
   queryParams.value.pageNum = 1;
   getList();
-}
+};
 /** 鐢熸垚浠g爜鎿嶄綔 */
 const handleGenTable = async (row?: TableVO) => {
   const tbIds = row?.tableId || ids.value;
-  if (tbIds == "") {
+  if (tbIds == '') {
     proxy?.$modal.msgError('璇烽�夋嫨瑕佺敓鎴愮殑鏁版嵁');
     return;
   }
-  if (row?.genType === "1") {
+  if (row?.genType === '1') {
     await genCode(row.tableId);
     proxy?.$modal.msgSuccess('鎴愬姛鐢熸垚鍒拌嚜瀹氫箟璺緞锛�' + row.genPath);
   } else {
     proxy?.$download.zip('/tool/gen/batchGenCode?tableIdStr=' + tbIds, 'ruoyi.zip');
   }
-}
+};
 /** 鍚屾鏁版嵁搴撴搷浣� */
 const handleSynchDb = async (row: TableVO) => {
   const tableId = row.tableId;
   await proxy?.$modal.confirm('纭瑕佸己鍒跺悓姝�"' + row.tableName + '"琛ㄧ粨鏋勫悧锛�');
   await synchDb(tableId);
   proxy?.$modal.msgSuccess('鍚屾鎴愬姛');
-}
+};
 /** 鎵撳紑瀵煎叆琛ㄥ脊绐� */
 const openImportTable = () => {
   importRef.value?.show(queryParams.value.dataName);
-}
+};
 /** 閲嶇疆鎸夐挳鎿嶄綔 */
 const resetQuery = () => {
   dateRange.value = ['', ''];
   queryFormRef.value?.resetFields();
   handleQuery();
-}
+};
 /** 棰勮鎸夐挳 */
 const handlePreview = async (row: TableVO) => {
   const res = await previewTable(row.tableId);
   preview.value.data = res.data;
   dialog.visible = true;
   preview.value.activeName = 'domain.java';
-}
+};
 /** 澶嶅埗浠g爜鎴愬姛 */
 const copyTextSuccess = () => {
   proxy?.$modal.msgSuccess('澶嶅埗鎴愬姛');
-}
+};
 // 澶氶�夋閫変腑鏁版嵁
 const handleSelectionChange = (selection: TableVO[]) => {
-  ids.value = selection.map(item => item.tableId);
+  ids.value = selection.map((item) => item.tableId);
   single.value = selection.length != 1;
   multiple.value = !selection.length;
-}
+};
 /** 淇敼鎸夐挳鎿嶄綔 */
 const handleEditTable = (row?: TableVO) => {
   const tableId = row?.tableId || ids.value[0];
   router.push({ path: '/tool/gen-edit/index/' + tableId, query: { pageNum: queryParams.value.pageNum } });
-}
+};
 /** 鍒犻櫎鎸夐挳鎿嶄綔 */
 const handleDelete = async (row?: TableVO) => {
   const tableIds = row?.tableId || ids.value;
@@ -242,10 +245,10 @@
   await delTable(tableIds);
   await getList();
   proxy?.$modal.msgSuccess('鍒犻櫎鎴愬姛');
-}
+};
 
 onMounted(() => {
   getList();
   getDataNameList();
-})
+});
 </script>
diff --git a/src/views/workflow/category/index.vue b/src/views/workflow/category/index.vue
new file mode 100644
index 0000000..fd3bfc5
--- /dev/null
+++ b/src/views/workflow/category/index.vue
@@ -0,0 +1,263 @@
+<template>
+  <div class="p-2">
+    <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+      <div v-show="showSearch" class="search">
+        <el-form ref="queryFormRef" :model="queryParams" :inline="true">
+          <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="categoryCode">
+            <el-input v-model="queryParams.categoryCode" 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>
+    </transition>
+
+    <el-card shadow="never">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+          <el-col :span="1.5">
+            <el-button v-hasPermi="['workflow:category:add']" type="primary" plain icon="Plus" @click="handleAdd()">鏂板</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button type="info" plain icon="Sort" @click="handleToggleExpandAll">灞曞紑/鎶樺彔</el-button>
+          </el-col>
+          <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
+        </el-row>
+      </template>
+      <el-table
+        ref="categoryTableRef"
+        v-loading="loading"
+        :data="categoryList"
+        row-key="id"
+        :default-expand-all="isExpandAll"
+        :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
+      >
+        <el-table-column label="鍒嗙被鍚嶇О" prop="categoryName" />
+        <el-table-column label="鍒嗙被缂栫爜" align="center" prop="categoryCode" />
+        <el-table-column label="鎺掑簭" align="center" prop="sortNum" />
+        <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-tooltip content="淇敼" placement="top">
+              <el-button v-hasPermi="['workflow:category:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)" />
+            </el-tooltip>
+            <el-tooltip content="鏂板" placement="top">
+              <el-button v-hasPermi="['workflow:category:add']" link type="primary" icon="Plus" @click="handleAdd(scope.row)" />
+            </el-tooltip>
+            <el-tooltip content="鍒犻櫎" placement="top">
+              <el-button v-hasPermi="['workflow:category:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)" />
+            </el-tooltip>
+          </template>
+        </el-table-column>
+      </el-table>
+    </el-card>
+    <!-- 娣诲姞鎴栦慨鏀规祦绋嬪垎绫诲璇濇 -->
+    <el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
+      <el-form ref="categoryFormRef" v-loading="loading" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="鐖剁骇鍒嗙被" prop="parentId">
+          <el-tree-select
+            v-model="form.parentId"
+            :data="categoryOptions"
+            :props="{ value: 'id', label: 'categoryName', children: 'children' }"
+            value-key="id"
+            placeholder="璇烽�夋嫨鐖剁骇id"
+            check-strictly
+          />
+        </el-form-item>
+        <el-form-item label="鍒嗙被鍚嶇О" prop="categoryName">
+          <el-input v-model="form.categoryName" placeholder="璇疯緭鍏ュ垎绫诲悕绉�" />
+        </el-form-item>
+        <el-form-item label="鍒嗙被缂栫爜" prop="categoryCode">
+          <el-input v-model="form.categoryCode" placeholder="璇疯緭鍏ュ垎绫荤紪鐮�" />
+        </el-form-item>
+        <el-form-item label="鎺掑簭" prop="sortNum">
+          <el-input-number v-model="form.sortNum" placeholder="璇疯緭鍏ユ帓搴�" controls-position="right" :min="0" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="buttonLoading" type="primary" @click="submitForm">纭� 瀹�</el-button>
+          <el-button @click="cancel">鍙� 娑�</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="Category" lang="ts">
+import { listCategory, getCategory, delCategory, addCategory, updateCategory } from '@/api/workflow/category';
+import { CategoryVO, CategoryQuery, CategoryForm } from '@/api/workflow/category/types';
+
+type CategoryOption = {
+  id: number;
+  categoryName: string;
+  children?: CategoryOption[];
+};
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const categoryList = ref<CategoryVO[]>([]);
+const categoryOptions = ref<CategoryOption[]>([]);
+const buttonLoading = ref(false);
+const showSearch = ref(true);
+const isExpandAll = ref(true);
+const loading = ref(false);
+
+const queryFormRef = ref<ElFormInstance>();
+const categoryFormRef = ref<ElFormInstance>();
+const categoryTableRef = ref<ElTableInstance>();
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: CategoryForm = {
+  id: undefined,
+  categoryName: undefined,
+  categoryCode: undefined,
+  parentId: undefined,
+  sortNum: 0
+};
+
+const data = reactive<PageData<CategoryForm, CategoryQuery>>({
+  form: { ...initFormData },
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    categoryName: undefined,
+    categoryCode: undefined
+  },
+  rules: {
+    id: [{ required: true, message: '涓婚敭涓嶈兘涓虹┖', trigger: 'blur' }],
+    categoryName: [{ required: true, message: '鍒嗙被鍚嶇О涓嶈兘涓虹┖', trigger: 'blur' }],
+    categoryCode: [{ required: true, message: '鍒嗙被缂栫爜涓嶈兘涓虹┖', trigger: 'blur' }],
+    parentId: [{ required: true, message: '鐖剁骇id涓嶈兘涓虹┖', trigger: 'blur' }]
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 鏌ヨ娴佺▼鍒嗙被鍒楄〃 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listCategory(queryParams.value);
+  const data = proxy?.handleTree<CategoryVO>(res.data, 'id', 'parentId');
+  if (data) {
+    categoryList.value = data;
+    loading.value = false;
+  }
+};
+
+/** 鏌ヨ娴佺▼鍒嗙被涓嬫媺鏍戠粨鏋� */
+const getTreeselect = async () => {
+  const res = await listCategory();
+  categoryOptions.value = [];
+  const data: CategoryOption = { id: 0, categoryName: '椤剁骇鑺傜偣', children: [] };
+  data.children = proxy?.handleTree<CategoryOption>(res.data, 'id', 'parentId');
+  categoryOptions.value.push(data);
+};
+
+// 鍙栨秷鎸夐挳
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+};
+
+// 琛ㄥ崟閲嶇疆
+const reset = () => {
+  form.value = { ...initFormData };
+  categoryFormRef.value?.resetFields();
+};
+
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+  getList();
+};
+
+/** 閲嶇疆鎸夐挳鎿嶄綔 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+};
+
+/** 鏂板鎸夐挳鎿嶄綔 */
+const handleAdd = (row?: CategoryVO) => {
+  dialog.visible = true;
+  dialog.title = '娣诲姞娴佺▼鍒嗙被';
+  nextTick(() => {
+    reset();
+    getTreeselect();
+    if (row != null && row.id) {
+      form.value.parentId = row.id;
+    } else {
+      form.value.parentId = 0;
+    }
+  });
+};
+
+/** 灞曞紑/鎶樺彔鎿嶄綔 */
+const handleToggleExpandAll = () => {
+  isExpandAll.value = !isExpandAll.value;
+  toggleExpandAll(categoryList.value, isExpandAll.value);
+};
+
+/** 灞曞紑/鎶樺彔鎿嶄綔 */
+const toggleExpandAll = (data: CategoryVO[], status: boolean) => {
+  data.forEach((item) => {
+    categoryTableRef.value?.toggleRowExpansion(item, status);
+    if (item.children && item.children.length > 0) toggleExpandAll(item.children, status);
+  });
+};
+
+/** 淇敼鎸夐挳鎿嶄綔 */
+const handleUpdate = (row: CategoryVO) => {
+  loading.value = true;
+  dialog.visible = true;
+  dialog.title = '淇敼娴佺▼鍒嗙被';
+  nextTick(async () => {
+    reset();
+    await getTreeselect();
+    if (row != null) {
+      form.value.parentId = row.id;
+    }
+    const res = await getCategory(row.id);
+    loading.value = false;
+    Object.assign(form.value, res.data);
+  });
+};
+
+/** 鎻愪氦鎸夐挳 */
+const submitForm = () => {
+  categoryFormRef.value.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      if (form.value.id) {
+        await updateCategory(form.value).finally(() => (buttonLoading.value = false));
+      } else {
+        await addCategory(form.value).finally(() => (buttonLoading.value = false));
+      }
+      proxy?.$modal.msgSuccess('鎿嶄綔鎴愬姛');
+      dialog.visible = false;
+      await getList();
+    }
+  });
+};
+
+/** 鍒犻櫎鎸夐挳鎿嶄綔 */
+const handleDelete = async (row: CategoryVO) => {
+  await proxy?.$modal.confirm('鏄惁纭鍒犻櫎娴佺▼鍒嗙被缂栧彿涓�"' + row.id + '"鐨勬暟鎹」锛�');
+  loading.value = true;
+  await delCategory(row.id).finally(() => (loading.value = false));
+  await getList();
+  proxy?.$modal.msgSuccess('鍒犻櫎鎴愬姛');
+};
+
+onMounted(() => {
+  getList();
+});
+</script>
diff --git a/src/views/workflow/formManage/index.vue b/src/views/workflow/formManage/index.vue
new file mode 100644
index 0000000..5057b3a
--- /dev/null
+++ b/src/views/workflow/formManage/index.vue
@@ -0,0 +1,243 @@
+<template>
+  <div class="p-2">
+    <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+      <div v-show="showSearch" class="search">
+        <el-form ref="queryFormRef" :model="queryParams" :inline="true">
+          <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>
+    </transition>
+
+    <el-card shadow="never">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+          <el-col :span="1.5">
+            <el-button v-hasPermi="['workflow:formManage:add']" type="primary" plain icon="Plus" @click="handleAdd">鏂板</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button v-hasPermi="['workflow:formManage:edit']" type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()"
+              >淇敼</el-button
+            >
+          </el-col>
+          <el-col :span="1.5">
+            <el-button v-hasPermi="['workflow:formManage:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()"
+              >鍒犻櫎</el-button
+            >
+          </el-col>
+          <el-col :span="1.5">
+            <el-button v-hasPermi="['workflow:formManage:export']" type="warning" plain icon="Download" @click="handleExport">瀵煎嚭</el-button>
+          </el-col>
+          <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" :data="formManageList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column label="琛ㄥ崟鍚嶇О" align="center" prop="formName" />
+        <el-table-column label="琛ㄥ崟绫诲瀷" align="center">
+          <template #default="scope">
+            <dict-tag :options="wf_form_type" :value="scope.row.formType"></dict-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="鍦板潃" align="center" prop="router" />
+        <el-table-column label="澶囨敞" align="center" prop="remork" />
+        <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-tooltip content="淇敼" placement="top">
+              <el-button v-hasPermi="['workflow:formManage:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
+            </el-tooltip>
+            <el-tooltip content="鍒犻櫎" placement="top">
+              <el-button v-hasPermi="['workflow:formManage:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
+    </el-card>
+    <!-- 娣诲姞鎴栦慨鏀硅〃鍗曠鐞嗗璇濇 -->
+    <el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
+      <el-form ref="formManageFormRef" :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="formType">
+          <el-radio-group v-model="form.formType" @change="form.router = ''">
+            <el-radio v-for="dict in wf_form_type" :key="dict.value" border :value="dict.value">{{ dict.label }}</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item v-if="form.formType === 'static'" label="璺敱鍦板潃" prop="router">
+          <el-input v-model="form.router" placeholder="璇疯緭鍏ヨ矾鐢卞湴鍧�" />
+        </el-form-item>
+        <el-form-item v-else label="琛ㄥ崟" prop="router">
+          <el-input v-model="form.router" disabled placeholder="璇烽�夋嫨琛ㄥ崟">
+            <template #append>
+              <el-button icon="Search" />
+            </template>
+          </el-input>
+        </el-form-item>
+        <el-form-item label="澶囨敞" prop="remork">
+          <el-input v-model="form.remork" type="textarea" placeholder="璇疯緭鍏ュ唴瀹�" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button :loading="buttonLoading" type="primary" @click="submitForm">纭� 瀹�</el-button>
+          <el-button @click="cancel">鍙� 娑�</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup name="FormManage" lang="ts">
+import { listFormManage, getFormManage, delFormManage, addFormManage, updateFormManage } from '@/api/workflow/formManage';
+import { FormManageVO, FormManageQuery, FormManageForm } from '@/api/workflow/formManage/types';
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { wf_form_type } = toRefs<any>(proxy?.useDict('wf_form_type'));
+
+const formManageList = ref<FormManageVO[]>([]);
+const buttonLoading = ref(false);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+
+const queryFormRef = ref<ElFormInstance>();
+const formManageFormRef = ref<ElFormInstance>();
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: FormManageForm = {
+  id: undefined,
+  formName: undefined,
+  formType: 'static',
+  remork: undefined
+};
+const data = reactive<PageData<FormManageForm, FormManageQuery>>({
+  form: { ...initFormData },
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    formName: undefined,
+    formType: undefined
+  },
+  rules: {
+    id: [{ required: true, message: '涓婚敭涓嶈兘涓虹┖', trigger: 'blur' }],
+    formName: [{ required: true, message: '琛ㄥ崟鍚嶇О涓嶈兘涓虹┖', trigger: 'blur' }],
+    formType: [{ required: true, message: '琛ㄥ崟绫诲瀷涓嶈兘涓虹┖', trigger: 'change' }],
+    router: [{ required: true, message: '涓嶈兘涓虹┖', trigger: 'blur' }]
+  }
+});
+
+const { queryParams, form, rules } = toRefs(data);
+
+/** 鏌ヨ琛ㄥ崟绠$悊鍒楄〃 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listFormManage(queryParams.value);
+  formManageList.value = res.rows;
+  total.value = res.total;
+  loading.value = false;
+};
+
+/** 鍙栨秷鎸夐挳 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+};
+
+/** 琛ㄥ崟閲嶇疆 */
+const reset = () => {
+  form.value = { ...initFormData };
+  formManageFormRef.value?.resetFields();
+};
+
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+};
+
+/** 閲嶇疆鎸夐挳鎿嶄綔 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+};
+
+/** 澶氶�夋閫変腑鏁版嵁 */
+const handleSelectionChange = (selection: FormManageVO[]) => {
+  ids.value = selection.map((item) => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+};
+
+/** 鏂板鎸夐挳鎿嶄綔 */
+const handleAdd = () => {
+  reset();
+  dialog.visible = true;
+  dialog.title = '娣诲姞琛ㄥ崟绠$悊';
+};
+
+/** 淇敼鎸夐挳鎿嶄綔 */
+const handleUpdate = async (row?: FormManageVO) => {
+  reset();
+  const _id = row?.id || ids.value[0];
+  const res = await getFormManage(_id);
+  Object.assign(form.value, res.data);
+  dialog.visible = true;
+  dialog.title = '淇敼琛ㄥ崟绠$悊';
+};
+
+/** 鎻愪氦鎸夐挳 */
+const submitForm = () => {
+  formManageFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      if (form.value.id) {
+        await updateFormManage(form.value).finally(() => (buttonLoading.value = false));
+      } else {
+        await addFormManage(form.value).finally(() => (buttonLoading.value = false));
+      }
+      proxy?.$modal.msgSuccess('鎿嶄綔鎴愬姛');
+      dialog.visible = false;
+      await getList();
+    }
+  });
+};
+
+/** 鍒犻櫎鎸夐挳鎿嶄綔 */
+const handleDelete = async (row?: FormManageVO) => {
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('鏄惁纭鍒犻櫎琛ㄥ崟绠$悊缂栧彿涓�"' + _ids + '"鐨勬暟鎹」锛�').finally(() => (loading.value = false));
+  await delFormManage(_ids);
+  proxy?.$modal.msgSuccess('鍒犻櫎鎴愬姛');
+  await getList();
+};
+
+/** 瀵煎嚭鎸夐挳鎿嶄綔 */
+const handleExport = () => {
+  proxy?.download(
+    'workflow/formManage/export',
+    {
+      ...queryParams.value
+    },
+    `formManage_${new Date().getTime()}.xlsx`
+  );
+};
+
+onMounted(() => {
+  getList();
+});
+</script>
diff --git a/src/views/workflow/leave/index.vue b/src/views/workflow/leave/index.vue
new file mode 100644
index 0000000..23d5442
--- /dev/null
+++ b/src/views/workflow/leave/index.vue
@@ -0,0 +1,251 @@
+<template>
+  <div class="p-2">
+    <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+      <div v-show="showSearch" class="search">
+        <el-form ref="queryFormRef" :model="queryParams" :inline="true">
+          <el-form-item label="璇峰亣澶╂暟" prop="startLeaveDays">
+            <el-input v-model="queryParams.startLeaveDays" placeholder="璇疯緭鍏ヨ鍋囧ぉ鏁�" clearable @keyup.enter="handleQuery" />
+          </el-form-item>
+          <el-form-item prop="endLeaveDays"> 鑷� </el-form-item>
+          <el-form-item prop="endLeaveDays">
+            <el-input v-model="queryParams.endLeaveDays" 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>
+    </transition>
+
+    <el-card shadow="never">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+          <el-col :span="1.5">
+            <el-button v-hasPermi="['demo:leave:add']" type="primary" plain icon="Plus" @click="handleAdd">鏂板</el-button>
+          </el-col>
+          <el-col :span="1.5">
+            <el-button v-hasPermi="['demo:leave:export']" type="warning" plain icon="Download" @click="handleExport">瀵煎嚭</el-button>
+          </el-col>
+          <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" :data="leaveList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column v-if="false" label="涓婚敭" align="center" prop="id" />
+        <el-table-column label="璇峰亣绫诲瀷" align="center">
+          <template #default="scope">
+            <el-tag>{{ options.find((e) => e.value === scope.row.leaveType)?.label }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="寮�濮嬫椂闂�" align="center" prop="startDate">
+          <template #default="scope">
+            <span>{{ parseTime(scope.row.startDate, '{y}-{m}-{d}') }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="缁撴潫鏃堕棿" align="center" prop="endDate">
+          <template #default="scope">
+            <span>{{ parseTime(scope.row.endDate, '{y}-{m}-{d}') }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="璇峰亣澶╂暟" align="center" prop="leaveDays" />
+        <el-table-column label="璇峰亣鍘熷洜" align="center" prop="remark" />
+        <el-table-column align="center" label="娴佺▼鐘舵��" min-width="70">
+          <template #default="scope">
+            <dict-tag :options="wf_business_status" :value="scope.row.processInstanceVo.businessStatus"></dict-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-button
+              v-if="
+                scope.row.processInstanceVo.businessStatus === 'draft' ||
+                scope.row.processInstanceVo.businessStatus === 'cancel' ||
+                scope.row.processInstanceVo.businessStatus === 'back'
+              "
+              v-hasPermi="['demo:leave:edit']"
+              size="small"
+              link
+              type="primary"
+              icon="Edit"
+              @click="handleUpdate(scope.row)"
+              >淇敼</el-button
+            >
+            <el-button
+              v-if="
+                scope.row.processInstanceVo.businessStatus === 'draft' ||
+                scope.row.processInstanceVo.businessStatus === 'cancel' ||
+                scope.row.processInstanceVo.businessStatus === 'back'
+              "
+              v-hasPermi="['demo:leave:remove']"
+              size="small"
+              link
+              type="primary"
+              icon="Delete"
+              @click="handleDelete(scope.row)"
+              >鍒犻櫎</el-button
+            >
+            <el-button link type="primary" size="small" icon="View" @click="handleView(scope.row)">鏌ョ湅</el-button>
+            <el-button
+              v-if="scope.row.processInstanceVo.businessStatus === 'waiting'"
+              link
+              size="small"
+              type="primary"
+              icon="Notification"
+              @click="handleCancelProcessApply(scope.row.processInstanceVo.id)"
+              >鎾ら攢</el-button
+            >
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
+    </el-card>
+  </div>
+</template>
+
+<script setup name="Leave" lang="ts">
+import { delLeave, listLeave } from '@/api/workflow/leave';
+import { cancelProcessApply } from '@/api/workflow/processInstance';
+import { LeaveForm, LeaveQuery, LeaveVO } from '@/api/workflow/leave/types';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { wf_business_status } = toRefs<any>(proxy?.useDict('wf_business_status'));
+const leaveList = ref<LeaveVO[]>([]);
+const loading = ref(true);
+const showSearch = ref(true);
+const ids = ref<Array<string | number>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const total = ref(0);
+const options = [
+  {
+    value: '1',
+    label: '浜嬪亣'
+  },
+  {
+    value: '2',
+    label: '璋冧紤'
+  },
+  {
+    value: '3',
+    label: '鐥呭亣'
+  },
+  {
+    value: '4',
+    label: '濠氬亣'
+  }
+];
+
+const queryFormRef = ref<ElFormInstance>();
+
+const data = reactive<PageData<LeaveForm, LeaveQuery>>({
+  form: {},
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    startLeaveDays: undefined,
+    endLeaveDays: undefined
+  },
+  rules: {}
+});
+
+const { queryParams } = toRefs(data);
+
+/** 鏌ヨ璇峰亣鍒楄〃 */
+const getList = async () => {
+  loading.value = true;
+  const res = await listLeave(queryParams.value);
+  leaveList.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: LeaveVO[]) => {
+  ids.value = selection.map((item) => item.id);
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+};
+
+/** 鏂板鎸夐挳鎿嶄綔 */
+const handleAdd = () => {
+  proxy.$tab.closePage(proxy.$route);
+  proxy.$router.push(`/demo/leaveEdit/index/add/add`);
+  proxy.$router.push({
+    path: `/demo/leaveEdit/index`,
+    query: {
+      type: 'add'
+    }
+  });
+};
+
+/** 淇敼鎸夐挳鎿嶄綔 */
+const handleUpdate = (row?: LeaveVO) => {
+  proxy.$tab.closePage(proxy.$route);
+  proxy.$router.push({
+    path: `/demo/leaveEdit/index`,
+    query: {
+      id: row.id,
+      type: 'update'
+    }
+  });
+};
+
+/** 鏌ョ湅鎸夐挳鎿嶄綔 */
+const handleView = (row?: LeaveVO) => {
+  proxy.$tab.closePage(proxy.$route);
+  proxy.$router.push({
+    path: `/demo/leaveEdit/index`,
+    query: {
+      id: row.id,
+      type: 'view'
+    }
+  });
+};
+
+/** 鍒犻櫎鎸夐挳鎿嶄綔 */
+const handleDelete = async (row?: LeaveVO) => {
+  const _ids = row?.id || ids.value;
+  await proxy?.$modal.confirm('鏄惁纭鍒犻櫎璇峰亣缂栧彿涓�"' + _ids + '"鐨勬暟鎹」锛�').finally(() => (loading.value = false));
+  await delLeave(_ids);
+  proxy?.$modal.msgSuccess('鍒犻櫎鎴愬姛');
+  await getList();
+};
+
+/** 瀵煎嚭鎸夐挳鎿嶄綔 */
+const handleExport = () => {
+  proxy?.download(
+    'demo/leave/export',
+    {
+      ...queryParams.value
+    },
+    `leave_${new Date().getTime()}.xlsx`
+  );
+};
+
+/** 鎾ら攢鎸夐挳鎿嶄綔 */
+const handleCancelProcessApply = async (id: string) => {
+  await proxy?.$modal.confirm('鏄惁纭鎾ら攢褰撳墠鍗曟嵁锛�');
+  loading.value = true;
+  await cancelProcessApply(id).finally(() => (loading.value = false));
+  await getList();
+  proxy?.$modal.msgSuccess('鎾ら攢鎴愬姛');
+};
+onMounted(() => {
+  getList();
+});
+</script>
diff --git a/src/views/workflow/leave/leaveEdit.vue b/src/views/workflow/leave/leaveEdit.vue
new file mode 100644
index 0000000..e978ad8
--- /dev/null
+++ b/src/views/workflow/leave/leaveEdit.vue
@@ -0,0 +1,273 @@
+<template>
+  <div class="p-2">
+    <el-card shadow="never">
+      <div style="display: flex; justify-content: space-between">
+        <div>
+          <el-button
+            v-if="
+              routeParams.type === 'add' ||
+              (routeParams.type === 'update' &&
+                form.processInstanceVo &&
+                form.processInstanceVo.businessStatus &&
+                (form.processInstanceVo.businessStatus === 'draft' ||
+                  form.processInstanceVo.businessStatus === 'cancel' ||
+                  form.processInstanceVo.businessStatus === 'back'))
+            "
+            :loading="buttonLoading"
+            type="info"
+            @click="submitForm('draft')"
+            >鏆傚瓨</el-button
+          >
+          <el-button
+            v-if="
+              routeParams.type === 'add' ||
+              (routeParams.type === 'update' &&
+                form.processInstanceVo &&
+                (form.processInstanceVo.businessStatus === 'draft' ||
+                  form.processInstanceVo.businessStatus === 'cancel' ||
+                  form.processInstanceVo.businessStatus === 'back'))
+            "
+            :loading="buttonLoading"
+            type="primary"
+            @click="submitForm('submit')"
+            >鎻� 浜�</el-button
+          >
+          <el-button
+            v-if="routeParams.type === 'approval' && form.processInstanceVo && form.processInstanceVo.businessStatus === 'waiting'"
+            :loading="buttonLoading"
+            type="primary"
+            @click="approvalVerifyOpen"
+            >瀹℃壒</el-button
+          >
+          <el-button v-if="processInstanceId" type="primary" @click="handleApprovalRecord">娴佺▼杩涘害</el-button>
+        </div>
+        <div>
+          <el-button style="float: right" @click="goBack()">杩斿洖</el-button>
+        </div>
+      </div>
+    </el-card>
+    <el-card shadow="never" style="height: 78vh; overflow-y: auto">
+      <el-form ref="leaveFormRef" v-loading="loading" :disabled="routeParams.type === 'view'" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="璇峰亣绫诲瀷" prop="leaveType">
+          <el-select v-model="form.leaveType" placeholder="璇烽�夋嫨璇峰亣绫诲瀷" style="width: 100%">
+            <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="璇峰亣鏃堕棿">
+          <el-date-picker
+            v-model="leaveTime"
+            type="daterange"
+            range-separator="To"
+            start-placeholder="寮�濮嬫椂闂�"
+            end-placeholder="缁撴潫鏃堕棿"
+            @change="changeLeaveTime()"
+          />
+        </el-form-item>
+        <el-form-item label="璇峰亣澶╂暟" prop="leaveDays">
+          <el-input v-model="form.leaveDays" disabled type="number" placeholder="璇疯緭鍏ヨ鍋囧ぉ鏁�" />
+        </el-form-item>
+        <el-form-item label="璇峰亣鍘熷洜" prop="remark">
+          <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="璇疯緭鍏ヨ鍋囧師鍥�" />
+        </el-form-item>
+      </el-form>
+    </el-card>
+    <!-- 鎻愪氦缁勪欢 -->
+    <submitVerify ref="submitVerifyRef" :task-variables="taskVariables" @submit-callback="submitCallback" />
+    <!-- 瀹℃壒璁板綍 -->
+    <approvalRecord ref="approvalRecordRef" />
+  </div>
+</template>
+
+<script setup name="Leave" lang="ts">
+import { addLeave, getLeave, updateLeave } from '@/api/workflow/leave';
+import { LeaveForm, LeaveQuery, LeaveVO } from '@/api/workflow/leave/types';
+import { startWorkFlow } from '@/api/workflow/task';
+import SubmitVerify from '@/components/Process/submitVerify.vue';
+import ApprovalRecord from '@/components/Process/approvalRecord.vue';
+import { AxiosResponse } from 'axios';
+import { StartProcessBo } from '@/api/workflow/workflowCommon/types';
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const buttonLoading = ref(false);
+const loading = ref(true);
+const leaveTime = ref<Array<string>>([]);
+//娴佺▼瀹炰緥id
+const processInstanceId = ref('');
+//璺敱鍙傛暟
+const routeParams = ref<Record<string, any>>({});
+const options = [
+  {
+    value: '1',
+    label: '浜嬪亣'
+  },
+  {
+    value: '2',
+    label: '璋冧紤'
+  },
+  {
+    value: '3',
+    label: '鐥呭亣'
+  },
+  {
+    value: '4',
+    label: '濠氬亣'
+  }
+];
+//鎻愪氦缁勪欢
+const submitVerifyRef = ref<InstanceType<typeof SubmitVerify>>();
+//瀹℃壒璁板綍缁勪欢
+const approvalRecordRef = ref<InstanceType<typeof ApprovalRecord>>();
+
+const leaveFormRef = ref<ElFormInstance>();
+
+const submitFormData = ref<StartProcessBo>({
+  businessKey: '',
+  tableName: '',
+  variables: {}
+});
+const taskVariables = ref<Record<string, any>>({});
+
+const initFormData: LeaveForm = {
+  id: undefined,
+  leaveType: undefined,
+  startDate: undefined,
+  endDate: undefined,
+  leaveDays: undefined,
+  remark: undefined,
+  processInstanceVo: {}
+};
+const data = reactive<PageData<LeaveForm, LeaveQuery>>({
+  form: { ...initFormData },
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    startLeaveDays: undefined,
+    endLeaveDays: undefined
+  },
+  rules: {
+    id: [{ required: true, message: '涓婚敭涓嶈兘涓虹┖', trigger: 'blur' }],
+    leaveType: [{ required: true, message: '璇峰亣绫诲瀷涓嶈兘涓虹┖', trigger: 'blur' }],
+    leaveTime: [{ required: true, message: '璇峰亣鏃堕棿涓嶈兘涓虹┖', trigger: 'blur' }],
+    leaveDays: [{ required: true, message: '璇峰亣澶╂暟涓嶈兘涓虹┖', trigger: 'blur' }]
+  }
+});
+
+const { form, rules } = toRefs(data);
+
+/** 琛ㄥ崟閲嶇疆 */
+const reset = () => {
+  form.value = { ...initFormData };
+  leaveTime.value = [];
+  leaveFormRef.value?.resetFields();
+};
+
+const changeLeaveTime = () => {
+  const startDate = new Date(leaveTime.value[0]).getTime();
+  const endDate = new Date(leaveTime.value[1]).getTime();
+  const diffInMilliseconds = endDate - startDate;
+  form.value.leaveDays = Math.floor(diffInMilliseconds / (1000 * 60 * 60 * 24));
+};
+/** 鑾峰彇璇︽儏 */
+const getInfo = () => {
+  loading.value = true;
+  buttonLoading.value = false;
+  nextTick(async () => {
+    const res = await getLeave(routeParams.value.id);
+    Object.assign(form.value, res.data);
+    leaveTime.value = [];
+    leaveTime.value.push(form.value.startDate);
+    leaveTime.value.push(form.value.endDate);
+    if (form.value.processInstanceVo) {
+      processInstanceId.value = form.value.processInstanceVo.id;
+    }
+    loading.value = false;
+    buttonLoading.value = false;
+  });
+};
+
+/** 鎻愪氦鎸夐挳 */
+const submitForm = (status: string) => {
+  if (leaveTime.value.length === 0) {
+    proxy?.$modal.msgError('璇峰亣鏃堕棿涓嶈兘涓虹┖');
+    return;
+  }
+  try {
+    leaveFormRef.value?.validate(async (valid: boolean) => {
+      form.value.startDate = leaveTime.value[0];
+      form.value.endDate = leaveTime.value[1];
+      if (valid) {
+        buttonLoading.value = true;
+        let res: AxiosResponse<LeaveVO>;
+        if (form.value.id) {
+          res = await updateLeave(form.value);
+        } else {
+          res = await addLeave(form.value);
+        }
+        form.value = res.data;
+        if (status === 'draft') {
+          buttonLoading.value = false;
+          proxy?.$modal.msgSuccess('鏆傚瓨鎴愬姛');
+          proxy.$tab.closePage(proxy.$route);
+          proxy.$router.go(-1);
+        } else {
+          await handleStartWorkFlow(res.data);
+        }
+      }
+    });
+  } finally {
+    buttonLoading.value = false;
+  }
+};
+
+//鎻愪氦鐢宠
+const handleStartWorkFlow = async (data: LeaveVO) => {
+  try {
+    submitFormData.value.tableName = 'test_leave';
+    submitFormData.value.businessKey = data.id;
+    //娴佺▼鍙橀噺
+    taskVariables.value = {
+      entity: data,
+      leaveDays: data.leaveDays,
+      userList: [1, 3],
+      userList2: [1, 3]
+    };
+    submitFormData.value.variables = taskVariables.value;
+    const resp = await startWorkFlow(submitFormData.value);
+    if (submitVerifyRef.value) {
+      buttonLoading.value = false;
+      submitVerifyRef.value.openDialog(resp.data.taskId);
+    }
+  } finally {
+    buttonLoading.value = false;
+  }
+};
+//瀹℃壒璁板綍
+const handleApprovalRecord = () => {
+  approvalRecordRef.value.init(processInstanceId.value);
+};
+//鎻愪氦鍥炶皟
+const submitCallback = async () => {
+  await proxy.$tab.closePage(proxy.$route);
+  proxy.$router.go(-1);
+};
+
+//杩斿洖
+const goBack = () => {
+  proxy.$tab.closePage(proxy.$route);
+  proxy.$router.go(-1);
+};
+//瀹℃壒
+const approvalVerifyOpen = async () => {
+  submitVerifyRef.value.openDialog(routeParams.value.taskId);
+};
+onMounted(() => {
+  nextTick(async () => {
+    routeParams.value = proxy.$route.query;
+    reset();
+    loading.value = false;
+    if (routeParams.value.type === 'update' || routeParams.value.type === 'view' || routeParams.value.type === 'approval') {
+      getInfo();
+    }
+  });
+});
+</script>
diff --git a/src/views/workflow/model/index.vue b/src/views/workflow/model/index.vue
new file mode 100644
index 0000000..e83cd94
--- /dev/null
+++ b/src/views/workflow/model/index.vue
@@ -0,0 +1,383 @@
+<template>
+  <div class="p-2">
+    <el-row :gutter="20">
+      <!-- 娴佺▼鍒嗙被鏍� -->
+      <el-col :lg="4" :xs="24" style="">
+        <el-card shadow="hover">
+          <el-input v-model="categoryName" placeholder="璇疯緭鍏ユ祦绋嬪垎绫诲悕" prefix-icon="Search" clearable />
+          <el-tree
+            ref="categoryTreeRef"
+            class="mt-2"
+            node-key="id"
+            :data="categoryOptions"
+            :props="{ label: 'categoryName', children: 'children' }"
+            :expand-on-click-node="false"
+            :filter-node-method="filterNode"
+            highlight-current
+            default-expand-all
+            @node-click="handleNodeClick"
+          ></el-tree>
+        </el-card>
+      </el-col>
+      <el-col :lg="20" :xs="24">
+        <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+          <div v-show="showSearch" class="mb-[10px]">
+            <el-card shadow="hover">
+              <el-form v-show="showSearch" ref="queryFormRef" :model="queryParams" :inline="true">
+                <el-form-item label="妯″瀷鍚嶇О" prop="name">
+                  <el-input v-model="queryParams.name" placeholder="璇疯緭鍏ユā鍨嬪悕绉�" clearable @keyup.enter="handleQuery" />
+                </el-form-item>
+                <el-form-item label="妯″瀷KEY" prop="key">
+                  <el-input v-model="queryParams.key" placeholder="璇疯緭鍏ユā鍨婯EY" 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>
+            </el-card>
+          </div>
+        </transition>
+        <el-card shadow="hover">
+          <template #header>
+            <el-row :gutter="10" class="mb8">
+              <el-col :span="1.5">
+                <el-button type="primary" plain icon="Plus" @click="handleAdd">鏂板</el-button>
+              </el-col>
+              <el-col :span="1.5">
+                <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()">淇敼</el-button>
+              </el-col>
+              <el-col :span="1.5">
+                <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()">鍒犻櫎</el-button>
+              </el-col>
+              <el-col :span="1.5">
+                <el-button type="primary" plain :disabled="multiple" icon="Download" @click="clickExportZip()">瀵煎嚭</el-button>
+              </el-col>
+              <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
+            </el-row>
+          </template>
+
+          <el-table v-loading="loading" border :data="modelList" @selection-change="handleSelectionChange">
+            <el-table-column type="selection" width="55" align="center" />
+            <el-table-column fixed align="center" type="index" label="搴忓彿" width="80"></el-table-column>
+            <el-table-column align="center" :show-overflow-tooltip="true" prop="name" label="妯″瀷鍚嶇О" width="200"></el-table-column>
+            <el-table-column align="center" prop="key" label="妯″瀷KEY"></el-table-column>
+            <el-table-column align="center" prop="version" label="鐗堟湰鍙�" width="90">
+              <template #default="scope"> v{{ scope.row.version }}.0</template>
+            </el-table-column>
+            <el-table-column align="center" prop="metaInfo" label="澶囨敞璇存槑" min-width="130"></el-table-column>
+            <el-table-column align="center" :show-overflow-tooltip="true" prop="createTime" label="鍒涘缓鏃堕棿" width="160"></el-table-column>
+            <el-table-column align="center" :show-overflow-tooltip="true" prop="lastUpdateTime" label="鏇存柊鏃堕棿" width="160"></el-table-column>
+            <el-table-column fixed="right" label="鎿嶄綔" align="center" width="170" class-name="small-padding fixed-width">
+              <template #default="scope">
+                <el-row :gutter="10" class="mb8">
+                  <el-col :span="1.5">
+                    <el-button link type="primary" size="small" icon="Pointer" @click="clickDesign(scope.row.id)">璁捐娴佺▼</el-button>
+                  </el-col>
+                  <el-col :span="1.5">
+                    <el-button link type="primary" size="small" icon="Delete" @click="handleDelete(scope.row)">鍒犻櫎</el-button>
+                  </el-col>
+                </el-row>
+                <el-row :gutter="10" class="mb8">
+                  <el-col :span="1.5">
+                    <el-button link type="primary" size="small" icon="ScaleToOriginal" @click="clickDeploy(scope.row.id, scope.row.key)">
+                      娴佺▼閮ㄧ讲
+                    </el-button>
+                  </el-col>
+                  <el-col :span="1.5">
+                    <el-button link type="primary" size="small" icon="CopyDocument" @click="handleCopy(scope.row)"> 澶嶅埗妯″瀷 </el-button>
+                  </el-col>
+                </el-row>
+              </template>
+            </el-table-column>
+          </el-table>
+          <pagination
+            v-show="total > 0"
+            v-model:page="queryParams.pageNum"
+            v-model:limit="queryParams.pageSize"
+            :total="total"
+            @pagination="getList"
+          />
+        </el-card>
+      </el-col>
+    </el-row>
+    <!-- 璁捐娴佺▼寮�濮� -->
+    <Design ref="designRef" @close-call-back="handleQuery"></Design>
+    <!-- 璁捐娴佺▼缁撴潫 -->
+    <!-- 娣诲姞妯″瀷瀵硅瘽妗� -->
+    <el-dialog v-model="dialog.visible" :title="dialog.title" width="650px" append-to-body :close-on-click-modal="false">
+      <el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
+        <el-form-item label="妯″瀷鍚嶇О锛�" prop="name">
+          <el-input v-model="form.name" :disabled="ids && ids.length > 0 && billType === 'update'" maxlength="20" show-word-limit />
+        </el-form-item>
+        <el-form-item label="妯″瀷KEY锛�" prop="key">
+          <el-input v-model="form.key" :disabled="ids && ids.length > 0 && billType === 'update'" maxlength="20" show-word-limit />
+        </el-form-item>
+        <el-form-item label="娴佺▼鍒嗙被" prop="categoryCode">
+          <el-tree-select
+            v-model="form.categoryCode"
+            :data="categoryOptions"
+            :props="{ value: 'categoryCode', label: 'categoryName', children: 'children' }"
+            value-key="categoryCode"
+            placeholder="璇烽�夋嫨娴佺▼鍒嗙被"
+            check-strictly
+          />
+        </el-form-item>
+        <el-form-item label="澶囨敞锛�" prop="description">
+          <el-input v-model="form.description" type="textarea" maxlength="200" show-word-limit></el-input>
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button type="primary" @click="submitForm">纭� 瀹�</el-button>
+          <el-button @click="cancel">鍙� 娑�</el-button>
+        </span>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script lang="ts" setup name="Model">
+import Design from '../../../components/BpmnDesign/index.vue';
+import { listModel, addModel, delModel, modelDeploy, getInfo, update } from '@/api/workflow/model';
+import { ModelQuery, ModelForm, ModelVO } from '@/api/workflow/model/types';
+import { listCategory } from '@/api/workflow/category';
+import { copyModel } from '@/api/workflow/model';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const formRef = ref<ElFormInstance>();
+const queryFormRef = ref<ElFormInstance>();
+const categoryTreeRef = ref<ElTreeInstance>();
+const designRef = ref<InstanceType<typeof Design>>();
+
+type CategoryOption = {
+  categoryCode: string;
+  categoryName: string;
+  children?: CategoryOption[];
+};
+
+const buttonLoading = ref(false);
+const loading = ref(true);
+const ids = ref<string[]>([]);
+const single = ref(true);
+const multiple = ref(true);
+const showSearch = ref(true);
+const total = ref(0);
+const modelList = ref<ModelVO[]>([]);
+const categoryOptions = ref<CategoryOption[]>([]);
+const categoryName = ref('');
+const billType = ref<string>('');
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const initFormData: ModelForm = {
+  id: '',
+  name: '',
+  key: '',
+  categoryCode: '',
+  xml: '',
+  svg: '',
+  description: ''
+};
+const data = reactive<PageData<ModelForm, ModelQuery>>({
+  form: { ...initFormData },
+  queryParams: {
+    pageNum: 1,
+    pageSize: 10,
+    name: '',
+    key: '',
+    categoryCode: ''
+  },
+  rules: {
+    name: [{ required: true, message: '妯″瀷涓嶈兘涓虹┖', trigger: 'blur' }],
+    key: [{ required: true, message: '妯″瀷KEY涓嶈兘涓虹┖', trigger: 'blur' }],
+    categoryCode: [{ required: true, message: '娴佺▼鍒嗙被涓嶈兘涓虹┖', trigger: 'blur' }]
+  }
+});
+const { queryParams, form, rules } = toRefs(data);
+
+onMounted(() => {
+  getList();
+  getTreeselect();
+});
+
+/** 鑺傜偣鍗曞嚮浜嬩欢 */
+const handleNodeClick = (data: ModelForm) => {
+  queryParams.value.categoryCode = data.categoryCode;
+  if (data.categoryCode === 'ALL') {
+    queryParams.value.categoryCode = '';
+  }
+  handleQuery();
+};
+/** 閫氳繃鏉′欢杩囨护鑺傜偣  */
+const filterNode = (value: string, data: any) => {
+  if (!value) return true;
+  return data.categoryName.indexOf(value) !== -1;
+};
+/** 鏍规嵁鍚嶇О绛涢�夐儴闂ㄦ爲 */
+watchEffect(
+  () => {
+    categoryTreeRef.value?.filter(categoryName.value);
+  },
+  {
+    flush: 'post' // watchEffect浼氬湪DOM鎸傝浇鎴栬�呮洿鏂颁箣鍓嶅氨浼氳Е鍙戯紝姝ゅ睘鎬ф帶鍒跺湪DOM鍏冪礌鏇存柊鍚庤繍琛�
+  }
+);
+
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+};
+/** 閲嶇疆鎸夐挳鎿嶄綔 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  queryParams.value.categoryCode = '';
+  queryParams.value.pageNum = 1;
+  queryParams.value.pageSize = 10;
+  handleQuery();
+};
+// 澶氶�夋閫変腑鏁版嵁
+const handleSelectionChange = (selection: ModelVO[]) => {
+  ids.value = selection.map((item: ModelVO) => item.id);
+  single.value = selection.length !== 1;
+  multiple.value = !selection.length;
+};
+//鍒嗛〉
+const getList = async () => {
+  loading.value = true;
+  const resp = await listModel(queryParams.value);
+  modelList.value = resp.rows;
+  total.value = resp.total;
+  loading.value = false;
+};
+/** 鍒犻櫎鎸夐挳鎿嶄綔 */
+const handleDelete = async (row?: ModelVO) => {
+  const id = row?.id || ids.value;
+  await proxy?.$modal.confirm('鏄惁纭鍒犻櫎妯″瀷id涓恒��' + id + '銆戠殑鏁版嵁椤癸紵');
+  loading.value = true;
+  await delModel(id).finally(() => (loading.value = false));
+  await getList();
+  proxy?.$modal.msgSuccess('鍒犻櫎鎴愬姛');
+};
+// 娴佺▼閮ㄧ讲
+const clickDeploy = async (id: string, key: string) => {
+  await proxy?.$modal.confirm('鏄惁閮ㄧ讲妯″瀷key涓恒��' + key + '銆戞祦绋嬶紵');
+  loading.value = true;
+  await modelDeploy(id).finally(() => (loading.value = false));
+  await getList();
+  proxy?.$modal.msgSuccess('閮ㄧ讲鎴愬姛');
+};
+//鏂板鎵撳紑
+const handleAdd = () => {
+  billType.value = 'add';
+  ids.value = [];
+  getTreeselect();
+  form.value = { ...initFormData };
+  dialog.visible = true;
+  dialog.title = '鏂板妯″瀷';
+};
+//淇敼鎵撳紑
+const handleUpdate = () => {
+  billType.value = 'update';
+  dialog.title = '淇敼妯″瀷';
+  nextTick(async () => {
+    await getTreeselect();
+    const _id = ids.value[0];
+    const res = await getInfo(_id);
+    Object.assign(form.value, res.data);
+    dialog.visible = true;
+  });
+};
+
+//澶嶅埗鎵撳紑
+const handleCopy = (row?: ModelVO) => {
+  billType.value = 'copy';
+  dialog.title = '澶嶅埗妯″瀷';
+  nextTick(async () => {
+    await getTreeselect();
+    form.value = { ...initFormData };
+    form.value.id = row.id;
+    dialog.visible = true;
+  });
+};
+
+/** 鎻愪氦鎸夐挳 */
+const submitForm = () => {
+  formRef.value.validate(async (valid: boolean) => {
+    if (valid) {
+      buttonLoading.value = true;
+      if ('copy' === billType.value) {
+        await copyModel(form.value);
+        proxy?.$modal.msgSuccess('鎿嶄綔鎴愬姛');
+      } else if (ids.value && ids.value.length > 0 && 'update' === billType.value) {
+        form.value.id = ids.value[0];
+        await update(form.value);
+        proxy?.$modal.msgSuccess('鎿嶄綔鎴愬姛');
+      } else {
+        initXml(form.value.key, form.value.name);
+        form.value.xml = xml.value;
+        await addModel(form.value);
+        proxy?.$modal.msgSuccess('鎿嶄綔鎴愬姛');
+      }
+      dialog.visible = false;
+      await getList();
+    }
+  });
+};
+
+/** 鍙栨秷鎸夐挳 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+};
+
+/** 琛ㄥ崟閲嶇疆 */
+const reset = () => {
+  form.value = { ...initFormData };
+  formRef.value.resetFields();
+};
+
+// 鎵撳紑璁捐娴佺▼
+const clickDesign = async (id: string) => {
+  await designRef.value.open(id);
+};
+// 瀵煎嚭娴佺▼妯″瀷
+const clickExportZip = () => {
+  proxy?.$download.zip('/workflow/model/export/zip/' + ids.value, '妯″瀷');
+};
+/** 鏌ヨ娴佺▼鍒嗙被涓嬫媺鏍戠粨鏋� */
+const getTreeselect = async () => {
+  const res = await listCategory();
+  categoryOptions.value = [];
+  const data: CategoryOption = { categoryCode: 'ALL', categoryName: '椤剁骇鑺傜偣', children: [] };
+  data.children = proxy?.handleTree<CategoryOption>(res.data, 'id', 'parentId');
+  categoryOptions.value.push(data);
+};
+
+const xml = ref<string>('');
+
+const initXml = async (key: string, name: string) => {
+  xml.value = `<?xml version="1.0" encoding="UTF-8"?>
+<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:bioc="http://bpmn.io/schema/bpmn/biocolor/1.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:flowable="http://flowable.org/bpmn" targetNamespace="http://www.flowable.org/processdef">
+  <process id="${key}" name="${name}">
+    <startEvent id="startNode1" name="寮�濮�" />
+  </process>
+  <bpmndi:BPMNDiagram id="BPMNDiagram_flow">
+    <bpmndi:BPMNPlane id="BPMNPlane_flow" bpmnElement="T-2d89e7a3-ba79-4abd-9f64-ea59621c258c">
+      <bpmndi:BPMNShape id="BPMNShape_startNode1" bpmnElement="startNode1" bioc:stroke="">
+        <omgdc:Bounds x="240" y="200" width="30" height="30" />
+        <bpmndi:BPMNLabel>
+          <omgdc:Bounds x="242" y="237" width="23" height="14" />
+        </bpmndi:BPMNLabel>
+      </bpmndi:BPMNShape>
+    </bpmndi:BPMNPlane>
+  </bpmndi:BPMNDiagram>
+</definitions>`;
+  return xml;
+};
+</script>
diff --git a/src/views/workflow/processDefinition/components/processPreview.vue b/src/views/workflow/processDefinition/components/processPreview.vue
new file mode 100644
index 0000000..19a95df
--- /dev/null
+++ b/src/views/workflow/processDefinition/components/processPreview.vue
@@ -0,0 +1,45 @@
+<template>
+  <el-dialog v-model="data.visible" title="棰勮" width="70%" append-to-body destroy-on-close>
+    <div v-if="data.type === 'bpmn' && data.xmlStr">
+      <BpmnViewer ref="bpmnViewerRef"></BpmnViewer>
+    </div>
+    <div v-if="data.type === 'xml' && data.xmlStr">
+      <highlightjs language="xml" :code="data.xmlStr" />
+    </div>
+    <template #footer>
+      <span v-if="data.type === 'xml'" class="dialog-footer"> </span>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup lang="ts">
+import BpmnViewer from '@/components/BpmnView/index.vue';
+
+const data = reactive({
+  visible: false,
+  type: '',
+  xmlStr: ''
+});
+
+const bpmnViewerRef = ref<InstanceType<typeof BpmnViewer>>();
+type PreviewType = 'xml' | 'bpmn';
+//鎵撳紑
+const openDialog = (xmlStr: string, type: PreviewType) => {
+  data.visible = true;
+  data.xmlStr = xmlStr;
+  data.type = type;
+  /** 娴佺▼鍥� */
+  if (type === 'bpmn') {
+    /** 蹇呴』鏀惧湪nextTick 鍚﹀垯绗竴娆℃墦寮�涓虹┖ */
+    nextTick(() => {
+      bpmnViewerRef.value?.initXml(data.xmlStr);
+    });
+  }
+};
+/**
+ * 瀵瑰鏆撮湶瀛愮粍浠舵柟娉�
+ */
+defineExpose({
+  openDialog
+});
+</script>
diff --git a/src/views/workflow/processDefinition/index.vue b/src/views/workflow/processDefinition/index.vue
new file mode 100644
index 0000000..1dacb80
--- /dev/null
+++ b/src/views/workflow/processDefinition/index.vue
@@ -0,0 +1,517 @@
+<template>
+  <div class="p-2">
+    <el-row :gutter="20">
+      <!-- 娴佺▼鍒嗙被鏍� -->
+      <el-col :lg="4" :xs="24" style="">
+        <el-card shadow="hover">
+          <el-input v-model="categoryName" placeholder="璇疯緭鍏ユ祦绋嬪垎绫诲悕" prefix-icon="Search" clearable />
+          <el-tree
+            ref="categoryTreeRef"
+            class="mt-2"
+            node-key="id"
+            :data="categoryOptions"
+            :props="{ label: 'categoryName', children: 'children' }"
+            :expand-on-click-node="false"
+            :filter-node-method="filterNode"
+            highlight-current
+            default-expand-all
+            @node-click="handleNodeClick"
+          ></el-tree>
+        </el-card>
+      </el-col>
+      <el-col :lg="20" :xs="24">
+        <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+          <div v-show="showSearch" class="mb-[10px]">
+            <el-card shadow="hover">
+              <el-form v-show="showSearch" ref="queryFormRef" :model="queryParams" :inline="true" label-width="120px">
+                <el-form-item label="娴佺▼瀹氫箟鍚嶇О" prop="name">
+                  <el-input v-model="queryParams.name" placeholder="璇疯緭鍏ユ祦绋嬪畾涔夊悕绉�" clearable @keyup.enter="handleQuery" />
+                </el-form-item>
+                <el-form-item label="娴佺▼瀹氫箟KEY" prop="key">
+                  <el-input v-model="queryParams.key" placeholder="璇疯緭鍏ユ祦绋嬪畾涔塊EY" 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>
+            </el-card>
+          </div>
+        </transition>
+        <el-card shadow="hover">
+          <template #header>
+            <el-row :gutter="10" class="mb8">
+              <el-col :span="1.5">
+                <el-button type="danger" icon="Delete" :disabled="multiple" @click="handleDelete()">鍒犻櫎</el-button>
+              </el-col>
+              <el-col :span="1.5">
+                <el-button type="primary" icon="UploadFilled" @click="uploadDialog.visible = true">閮ㄧ讲娴佺▼鏂囦欢</el-button>
+              </el-col>
+              <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>
+            </el-row>
+          </template>
+
+          <el-table v-loading="loading" border :data="processDefinitionList" @selection-change="handleSelectionChange">
+            <el-table-column type="selection" width="55" align="center" />
+            <el-table-column align="center" type="index" label="搴忓彿" width="60"></el-table-column>
+            <el-table-column align="center" prop="name" label="娴佺▼瀹氫箟鍚嶇О" :show-overflow-tooltip="true"></el-table-column>
+            <el-table-column align="center" prop="key" label="鏍囪瘑KEY" width="80"></el-table-column>
+            <el-table-column align="center" prop="version" label="鐗堟湰鍙�" width="80">
+              <template #default="scope"> v{{ scope.row.version }}.0</template>
+            </el-table-column>
+            <el-table-column align="center" prop="resourceName" label="娴佺▼XML" width="100" :show-overflow-tooltip="true">
+              <template #default="scope">
+                <el-link type="primary" @click="clickPreview(scope.row.id, 'xml')">{{ scope.row.resourceName }}</el-link>
+              </template>
+            </el-table-column>
+            <el-table-column align="center" prop="diagramResourceName" label="娴佺▼鍥剧墖" width="100" :show-overflow-tooltip="true">
+              <template #default="scope">
+                <el-link type="primary" @click="clickPreview(scope.row.id, 'bpmn')">{{ scope.row.diagramResourceName }}</el-link>
+              </template>
+            </el-table-column>
+            <el-table-column align="center" prop="suspensionState" label="鐘舵��" width="80">
+              <template #default="scope">
+                <el-tag v-if="scope.row.suspensionState == 1" type="success">婵�娲�</el-tag>
+                <el-tag v-else type="danger">鎸傝捣</el-tag>
+              </template>
+            </el-table-column>
+            <el-table-column align="center" prop="deploymentTime" label="閮ㄧ讲鏃堕棿" width="120" :show-overflow-tooltip="true"></el-table-column>
+            <el-table-column align="center" label="琛ㄥ悕/琛ㄥ崟KEY" width="120" :show-overflow-tooltip="true">
+              <template #default="scope">
+                <span v-if="scope.row.wfDefinitionConfigVo">
+                  {{ scope.row.wfDefinitionConfigVo.tableName }}
+                </span>
+              </template>
+            </el-table-column>
+            <el-table-column fixed="right" label="鎿嶄綔" align="center" width="220" class-name="small-padding fixed-width">
+              <template #default="scope">
+                <el-row :gutter="10" class="mb8">
+                  <el-col :span="1.5">
+                    <el-button
+                      link
+                      type="primary"
+                      size="small"
+                      :icon="scope.row.suspensionState === 1 ? 'Lock' : 'Unlock'"
+                      @click="handleProcessDefState(scope.row)"
+                    >
+                      {{ scope.row.suspensionState === 1 ? '鎸傝捣娴佺▼' : '婵�娲绘祦绋�' }}
+                    </el-button>
+                  </el-col>
+                  <el-col :span="1.5">
+                    <el-button link type="primary" size="small" icon="Document" @click="getProcessDefinitionHitoryList(scope.row.id, scope.row.key)">
+                      鍘嗗彶鐗堟湰
+                    </el-button>
+                  </el-col>
+                  <el-col :span="1.5">
+                    <el-button link type="primary" size="small" icon="Delete" @click="handleDelete(scope.row)">鍒犻櫎</el-button>
+                  </el-col>
+                </el-row>
+                <el-row :gutter="10" class="mb8">
+                  <el-col :span="1.5">
+                    <el-button link type="primary" size="small" icon="Sort" @click="handleConvertToModel(scope.row)"> 杞崲妯″瀷 </el-button>
+                  </el-col>
+                  <el-col :span="1.5">
+                    <el-button link type="primary" size="small" icon="Tickets" @click="handleDefinitionConfigOpen(scope.row)">缁戝畾涓氬姟</el-button>
+                  </el-col>
+                </el-row>
+              </template>
+            </el-table-column>
+          </el-table>
+          <pagination
+            v-show="total > 0"
+            v-model:page="queryParams.pageNum"
+            v-model:limit="queryParams.pageSize"
+            :total="total"
+            @pagination="getList"
+          />
+        </el-card>
+      </el-col>
+    </el-row>
+    <!-- 棰勮鍥剧墖鎴杧ml -->
+    <process-preview ref="previewRef" />
+
+    <!-- 閮ㄧ讲鏂囦欢 -->
+    <el-dialog v-if="uploadDialog.visible" v-model="uploadDialog.visible" :title="uploadDialog.title" width="30%">
+      <div v-loading="uploadDialogLoading">
+        <div class="mb5">
+          <el-text class="mx-1" size="large"><span class="text-danger">*</span>璇烽�夋嫨閮ㄧ讲娴佺▼鍒嗙被锛�</el-text>
+          <el-tree-select
+            v-model="selectCategory"
+            :data="categoryOptions"
+            :props="{ value: 'categoryCode', label: 'categoryName', children: 'children' }"
+            filterable
+            value-key="categoryCode"
+            :render-after-expand="false"
+            check-strictly
+            style="width: 240px"
+          />
+        </div>
+        <el-upload
+          class="upload-demo"
+          drag
+          multiple
+          accept="application/zip,application/xml,.bpmn"
+          :before-upload="handlerBeforeUpload"
+          :http-request="handerDeployProcessFile"
+        >
+          <el-icon class="UploadFilled"><upload-filled /></el-icon>
+          <div class="el-upload__text"><em>鐐瑰嚮涓婁紶锛岄�夋嫨BPMN娴佺▼鏂囦欢</em></div>
+          <div class="el-upload__text">浠呮敮鎸� .zip銆�.bpmn20.xml銆乥pmn 鏍煎紡鏂囦欢</div>
+          <div class="el-upload__text">PS:濡傝嫢閮ㄧ讲璇烽儴缃蹭粠鏈」鐩ā鍨嬬鐞嗗鍑虹殑鏁版嵁</div>
+        </el-upload>
+      </div>
+    </el-dialog>
+
+    <!-- 鍘嗗彶鐗堟湰 -->
+    <el-dialog v-if="processDefinitionDialog.visible" v-model="processDefinitionDialog.visible" :title="processDefinitionDialog.title" width="70%">
+      <el-table v-loading="loading" :data="processDefinitionHistoryList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column align="center" type="index" label="搴忓彿" width="60"></el-table-column>
+        <el-table-column align="center" prop="name" label="娴佺▼瀹氫箟鍚嶇О" :show-overflow-tooltip="true" min-width="80"></el-table-column>
+        <el-table-column align="center" prop="key" label="鏍囪瘑KEY"></el-table-column>
+        <el-table-column align="center" prop="version" label="鐗堟湰鍙�" width="90">
+          <template #default="scope"> v{{ scope.row.version }}.0</template>
+        </el-table-column>
+        <el-table-column align="center" prop="resourceName" label="娴佺▼XML" min-width="80" :show-overflow-tooltip="true">
+          <template #default="scope">
+            <el-link type="primary" @click="clickPreviewXML(scope.row.id)">{{ scope.row.resourceName }}</el-link>
+          </template>
+        </el-table-column>
+        <el-table-column align="center" prop="diagramResourceName" label="娴佺▼鍥剧墖" min-width="80" :show-overflow-tooltip="true">
+          <template #default="scope">
+            <el-link type="primary" @click="clickPreviewImg(scope.row.id)">{{ scope.row.diagramResourceName }}</el-link>
+          </template>
+        </el-table-column>
+        <el-table-column align="center" prop="suspensionState" label="鐘舵��" min-width="70">
+          <template #default="scope">
+            <el-tag v-if="scope.row.suspensionState == 1" type="success">婵�娲�</el-tag>
+            <el-tag v-else type="danger">鎸傝捣</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column align="center" prop="deploymentTime" label="閮ㄧ讲鏃堕棿" :show-overflow-tooltip="true"></el-table-column>
+        <el-table-column fixed="right" label="鎿嶄綔" align="center" width="200" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-row :gutter="10" class="mb8">
+              <el-col :span="1.5">
+                <el-button
+                  link
+                  type="primary"
+                  size="small"
+                  :icon="scope.row.suspensionState === 1 ? 'Lock' : 'Unlock'"
+                  @click="handleProcessDefState(scope.row)"
+                >
+                  {{ scope.row.suspensionState === 1 ? '鎸傝捣娴佺▼' : '婵�娲绘祦绋�' }}
+                </el-button>
+              </el-col>
+              <el-col :span="1.5">
+                <el-button type="text" size="small" icon="Tickets" @click="handleDefinitionConfigOpen(scope.row)">缁戝畾涓氬姟</el-button>
+              </el-col>
+            </el-row>
+            <el-row :gutter="10" class="mb8">
+              <el-col :span="1.5">
+                <el-button link type="primary" icon="Sort" size="small" @click="handleConvertToModel(scope.row)"> 杞崲妯″瀷 </el-button>
+              </el-col>
+              <el-col :span="1.5">
+                <el-button link type="primary" icon="Delete" size="small" @click="handleDelete(scope.row)">鍒犻櫎</el-button>
+              </el-col>
+            </el-row>
+          </template>
+        </el-table-column>
+      </el-table>
+    </el-dialog>
+
+    <!-- 琛ㄥ崟閰嶇疆 -->
+    <el-dialog
+      v-model="definitionConfigDialog.visible"
+      :title="definitionConfigDialog.title"
+      width="650px"
+      append-to-body
+      :close-on-click-modal="false"
+    >
+      <el-form :model="definitionConfigForm" label-width="auto">
+        <el-form-item label="娴佺▼KEY">
+          <el-input v-model="definitionConfigForm.processKey" disabled />
+        </el-form-item>
+        <el-form-item label="琛ㄥ悕" prop="formId">
+          <el-input v-model="definitionConfigForm.tableName" placeholder="绀轰緥:test_leave" />
+        </el-form-item>
+        <el-form-item label="澶囨敞">
+          <el-input v-model="definitionConfigForm.remark" type="textarea" resize="none" />
+        </el-form-item>
+      </el-form>
+
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="definitionConfigDialog.visible = false">鍙栨秷</el-button>
+          <el-button type="primary" @click="handlerSaveForm">淇濆瓨</el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script lang="ts" setup name="processDefinition">
+import {
+  listProcessDefinition,
+  definitionImage,
+  definitionXml,
+  deleteProcessDefinition,
+  updateDefinitionState,
+  convertToModel,
+  deployProcessFile,
+  getListByKey
+} from '@/api/workflow/processDefinition';
+import { getByTableNameNotDefId, getByDefId, saveOrUpdate } from '@/api/workflow/definitionConfig';
+import ProcessPreview from './components/processPreview.vue';
+import { listCategory } from '@/api/workflow/category';
+import { CategoryVO } from '@/api/workflow/category/types';
+import { ProcessDefinitionQuery, ProcessDefinitionVO } from '@/api/workflow/processDefinition/types';
+import { DefinitionConfigForm } from '@/api/workflow/definitionConfig/types';
+import { UploadRequestOptions, ElMessage, ElMessageBox } from 'element-plus';
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+
+const previewRef = ref<InstanceType<typeof ProcessPreview>>();
+const queryFormRef = ref<ElFormInstance>();
+const categoryTreeRef = ref<ElTreeInstance>();
+const definitionConfigForm = ref<DefinitionConfigForm>({});
+
+type CategoryOption = {
+  categoryCode: string;
+  categoryName: string;
+  children?: CategoryOption[];
+};
+
+const loading = ref(true);
+const ids = ref<Array<any>>([]);
+const deploymentIds = ref<Array<any>>([]);
+const keys = ref<Array<any>>([]);
+const single = ref(true);
+const multiple = ref(true);
+const showSearch = ref(true);
+const total = ref(0);
+const uploadDialogLoading = ref(false);
+const processDefinitionList = ref<ProcessDefinitionVO[]>([]);
+const processDefinitionHistoryList = ref<ProcessDefinitionVO[]>([]);
+const categoryOptions = ref<CategoryOption[]>([]);
+const categoryName = ref('');
+/** 閮ㄧ讲鏂囦欢鍒嗙被閫夋嫨 */
+const selectCategory = ref();
+
+const uploadDialog = reactive<DialogOption>({
+  visible: false,
+  title: '閮ㄧ讲娴佺▼鏂囦欢'
+});
+
+const processDefinitionDialog = reactive<DialogOption>({
+  visible: false,
+  title: '鍘嗗彶鐗堟湰'
+});
+
+const definitionConfigDialog = reactive<DialogOption>({
+  visible: false,
+  title: '娴佺▼瀹氫箟閰嶇疆'
+});
+
+// 鏌ヨ鍙傛暟
+const queryParams = ref<ProcessDefinitionQuery>({
+  pageNum: 1,
+  pageSize: 10,
+  name: undefined,
+  key: undefined,
+  categoryCode: undefined
+});
+
+onMounted(() => {
+  getList();
+  getTreeselect();
+});
+
+/** 鑺傜偣鍗曞嚮浜嬩欢 */
+const handleNodeClick = (data: CategoryVO) => {
+  queryParams.value.categoryCode = data.categoryCode;
+  if (data.categoryCode === 'ALL') {
+    queryParams.value.categoryCode = '';
+  }
+  handleQuery();
+};
+/** 閫氳繃鏉′欢杩囨护鑺傜偣  */
+const filterNode = (value: string, data: any) => {
+  if (!value) return true;
+  return data.categoryName.indexOf(value) !== -1;
+};
+/** 鏍规嵁鍚嶇О绛涢�夐儴闂ㄦ爲 */
+watchEffect(
+  () => {
+    categoryTreeRef.value.filter(categoryName.value);
+  },
+  {
+    flush: 'post' // watchEffect浼氬湪DOM鎸傝浇鎴栬�呮洿鏂颁箣鍓嶅氨浼氳Е鍙戯紝姝ゅ睘鎬ф帶鍒跺湪DOM鍏冪礌鏇存柊鍚庤繍琛�
+  }
+);
+
+/** 鏌ヨ娴佺▼鍒嗙被涓嬫媺鏍戠粨鏋� */
+const getTreeselect = async () => {
+  const res = await listCategory();
+  categoryOptions.value = [];
+  const data: CategoryOption = { categoryCode: 'ALL', categoryName: '椤剁骇鑺傜偣', children: [] };
+  data.children = proxy?.handleTree<CategoryOption>(res.data, 'id', 'parentId');
+  categoryOptions.value.push(data);
+};
+
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+  queryParams.value.pageNum = 1;
+  getList();
+};
+/** 閲嶇疆鎸夐挳鎿嶄綔 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  queryParams.value.categoryCode = '';
+  queryParams.value.pageNum = 1;
+  queryParams.value.pageSize = 10;
+  handleQuery();
+};
+// 澶氶�夋閫変腑鏁版嵁
+const handleSelectionChange = (selection: any) => {
+  ids.value = selection.map((item: any) => item.id);
+  deploymentIds.value = selection.map((item: any) => item.deploymentId);
+  keys.value = selection.map((item: any) => item.key);
+  single.value = selection.length !== 1;
+  multiple.value = !selection.length;
+};
+//鍒嗛〉
+const getList = async () => {
+  loading.value = true;
+  const resp = await listProcessDefinition(queryParams.value);
+  processDefinitionList.value = resp.rows;
+  total.value = resp.total;
+  loading.value = false;
+};
+//鑾峰彇鍘嗗彶娴佺▼瀹氫箟
+const getProcessDefinitionHitoryList = async (id: string, key: string) => {
+  processDefinitionDialog.visible = true;
+  loading.value = true;
+  const resp = await getListByKey(key);
+  if (resp.data && resp.data.length > 0) {
+    processDefinitionHistoryList.value = resp.data.filter((item: any) => item.id !== id);
+  }
+  loading.value = false;
+};
+
+type PreviewType = 'xml' | 'bpmn';
+//棰勮 鍏叡鏂规硶
+const clickPreview = async (id: string, type: PreviewType) => {
+  loading.value = true;
+  const resp = await definitionXml(id);
+  if (previewRef.value) {
+    const xmlStr = resp.data.xmlStr;
+    loading.value = false;
+    previewRef.value.openDialog(xmlStr, type);
+  }
+};
+
+/** 鍒犻櫎鎸夐挳鎿嶄綔 */
+const handleDelete = async (row?: ProcessDefinitionVO) => {
+  const id = row?.id || ids.value;
+  const deployIds = row?.deploymentId || deploymentIds.value;
+  const defKeys = row?.key || keys.value;
+  await proxy?.$modal.confirm('鏄惁纭鍒犻櫎娴佺▼瀹氫箟KEY涓恒��' + defKeys + '銆戠殑鏁版嵁椤癸紵');
+  loading.value = true;
+  await deleteProcessDefinition(deployIds, id).finally(() => (loading.value = false));
+  await getList();
+  proxy?.$modal.msgSuccess('鍒犻櫎鎴愬姛');
+};
+/** 鎸傝捣/婵�娲� */
+const handleProcessDefState = async (row: ProcessDefinitionVO) => {
+  let msg: string;
+  if (row.suspensionState === 1) {
+    msg = `鏆傚仠鍚庯紝姝ゆ祦绋嬩笅鐨勬墍鏈変换鍔¢兘涓嶅厑璁稿線鍚庢祦杞紝鎮ㄧ‘瀹氭寕璧枫��${row.name || row.key}銆戝悧锛焋;
+  } else {
+    msg = `鍚姩鍚庯紝姝ゆ祦绋嬩笅鐨勬墍鏈変换鍔¢兘鍏佽寰�鍚庢祦杞紝鎮ㄧ‘瀹氭縺娲汇��${row.name || row.key}銆戝悧锛焋;
+  }
+  await proxy?.$modal.confirm(msg);
+  loading.value = true;
+  await updateDefinitionState(row.id).finally(() => (loading.value = false));
+  await getList();
+  proxy?.$modal.msgSuccess('鎿嶄綔鎴愬姛');
+};
+/** 娴佺▼瀹氫箟杞崲涓烘ā鍨� */
+const handleConvertToModel = async (row: ProcessDefinitionVO) => {
+  await proxy?.$modal.confirm('鏄惁纭杞崲娴佺▼瀹氫箟key涓恒��' + row.key + '銆戠殑鏁版嵁椤癸紵');
+  await convertToModel(row.id).finally(() => (loading.value = false));
+  getList();
+  proxy?.$modal.msgSuccess('鎿嶄綔鎴愬姛');
+};
+
+//涓婁紶鏂囦欢鍓嶇殑閽╁瓙
+const handlerBeforeUpload = () => {
+  if (selectCategory.value === 'ALL') {
+    proxy?.$modal.msgError('椤剁骇鑺傜偣涓嶅彲浣滀负鍒嗙被锛�');
+    return false;
+  }
+  if (!selectCategory.value) {
+    proxy?.$modal.msgError('璇烽�夋嫨宸︿晶瑕佷笂浼犵殑鍒嗙被锛�');
+    return false;
+  }
+};
+//閮ㄧ讲鏂囦欢
+const handerDeployProcessFile = (data: UploadRequestOptions): XMLHttpRequest => {
+  let formData = new FormData();
+  uploadDialogLoading.value = true;
+  formData.append('file', data.file);
+  formData.append('categoryCode', selectCategory.value);
+  deployProcessFile(formData)
+    .then(() => {
+      uploadDialog.visible = false;
+      proxy?.$modal.msgSuccess('閮ㄧ讲鎴愬姛');
+      handleQuery();
+    })
+    .finally(() => {
+      uploadDialogLoading.value = false;
+    });
+  return;
+};
+//鎵撳紑娴佺▼瀹氫箟閰嶇疆
+const handleDefinitionConfigOpen = async (row: ProcessDefinitionVO) => {
+  definitionConfigDialog.visible = true;
+  definitionConfigForm.value.processKey = row.key;
+  definitionConfigForm.value.definitionId = row.id;
+  definitionConfigForm.value.version = row.version;
+  const resp = await getByDefId(row.id);
+  if (resp.data) {
+    definitionConfigForm.value = resp.data;
+  } else {
+    definitionConfigForm.value.tableName = undefined;
+    definitionConfigForm.value.remark = undefined;
+  }
+};
+//淇濆瓨琛ㄥ崟
+const handlerSaveForm = async () => {
+  getByTableNameNotDefId(definitionConfigForm.value.tableName, definitionConfigForm.value.definitionId).then((res) => {
+    if (res.data && res.data.length > 0) {
+      ElMessageBox.confirm('琛ㄥ悕宸茶銆�' + res.data[0].processKey + '銆戠増鏈瑅' + res.data[0].version + '.0缁戝畾纭鍚庡皢浼氬垹闄ょ粦瀹氱殑娴佺▼KEY!', '鎻愮ず', {
+        confirmButtonText: '纭',
+        cancelButtonText: '鍙栨秷',
+        type: 'warning'
+      }).then(() => {
+        saveOrUpdate(definitionConfigForm.value).then((resp) => {
+          if (resp.code === 200) {
+            proxy?.$modal.msgSuccess('鎿嶄綔鎴愬姛');
+            definitionConfigDialog.visible = false;
+            getList();
+          }
+        });
+      });
+    } else {
+      saveOrUpdate(definitionConfigForm.value).then((resp) => {
+        if (resp.code === 200) {
+          proxy?.$modal.msgSuccess('鎿嶄綔鎴愬姛');
+          definitionConfigDialog.visible = false;
+          getList();
+        }
+      });
+    }
+  });
+};
+</script>
diff --git a/src/views/workflow/processInstance/index.vue b/src/views/workflow/processInstance/index.vue
new file mode 100644
index 0000000..4f994af
--- /dev/null
+++ b/src/views/workflow/processInstance/index.vue
@@ -0,0 +1,361 @@
+<template>
+  <div class="p-2">
+    <el-row :gutter="20">
+      <!-- 娴佺▼鍒嗙被鏍� -->
+      <el-col :lg="4" :xs="24" style="">
+        <el-card shadow="hover">
+          <el-input v-model="categoryName" placeholder="璇疯緭鍏ユ祦绋嬪垎绫诲悕" prefix-icon="Search" clearable />
+          <el-tree
+            ref="categoryTreeRef"
+            class="mt-2"
+            node-key="id"
+            :data="categoryOptions"
+            :props="{ label: 'categoryName', children: 'children' }"
+            :expand-on-click-node="false"
+            :filter-node-method="filterNode"
+            highlight-current
+            default-expand-all
+            @node-click="handleNodeClick"
+          ></el-tree>
+        </el-card>
+      </el-col>
+      <el-col :lg="20" :xs="24">
+        <div class="mb-[10px]">
+          <el-card shadow="hover" class="text-center">
+            <el-radio-group v-model="tab" @change="changeTab(tab)">
+              <el-radio-button value="running">杩愯涓�</el-radio-button>
+              <el-radio-button value="finish">宸插畬鎴�</el-radio-button>
+            </el-radio-group>
+          </el-card>
+        </div>
+        <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+          <div v-show="showSearch" class="mb-[10px]">
+            <el-card shadow="hover">
+              <el-form v-show="showSearch" ref="queryFormRef" :model="queryParams" :inline="true" label-width="120px">
+                <el-form-item label="娴佺▼瀹氫箟鍚嶇О" prop="name">
+                  <el-input v-model="queryParams.name" placeholder="璇疯緭鍏ユ祦绋嬪畾涔夊悕绉�" @keyup.enter="handleQuery" />
+                </el-form-item>
+                <el-form-item label="娴佺▼瀹氫箟KEY" prop="key">
+                  <el-input v-model="queryParams.key" placeholder="璇疯緭鍏ユ祦绋嬪畾涔塊EY" @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>
+            </el-card>
+          </div>
+        </transition>
+        <el-card shadow="hover">
+          <template #header>
+            <el-row :gutter="10" class="mb8">
+              <el-col :span="1.5">
+                <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete">鍒犻櫎</el-button>
+              </el-col>
+              <right-toolbar v-model:showSearch="showSearch" @query-table="handleQuery"></right-toolbar>
+            </el-row>
+          </template>
+
+          <el-table v-loading="loading" border :data="processInstanceList" @selection-change="handleSelectionChange">
+            <el-table-column type="selection" width="55" align="center" />
+            <el-table-column align="center" type="index" label="搴忓彿" width="60"></el-table-column>
+            <el-table-column :show-overflow-tooltip="true" align="center" label="娴佺▼瀹氫箟鍚嶇О">
+              <template #default="scope">
+                <span>{{ scope.row.processDefinitionName }}v{{ scope.row.processDefinitionVersion }}.0</span>
+              </template>
+            </el-table-column>
+            <el-table-column align="center" prop="processDefinitionKey" label="娴佺▼瀹氫箟KEY"></el-table-column>
+            <el-table-column align="center" prop="processDefinitionVersion" label="鐗堟湰鍙�" width="90">
+              <template #default="scope"> v{{ scope.row.processDefinitionVersion }}.0</template>
+            </el-table-column>
+            <el-table-column v-if="tab === 'running'" align="center" prop="isSuspended" label="鐘舵��" min-width="70">
+              <template #default="scope">
+                <el-tag v-if="!scope.row.isSuspended" type="success">婵�娲�</el-tag>
+                <el-tag v-else type="danger">鎸傝捣</el-tag>
+              </template>
+            </el-table-column>
+            <el-table-column align="center" label="娴佺▼鐘舵��" min-width="70">
+              <template #default="scope">
+                <dict-tag :options="wf_business_status" :value="scope.row.businessStatus"></dict-tag>
+              </template>
+            </el-table-column>
+            <el-table-column align="center" prop="startTime" label="鍚姩鏃堕棿" width="160"></el-table-column>
+            <el-table-column v-if="tab === 'finish'" align="center" prop="endTime" label="缁撴潫鏃堕棿" width="160"></el-table-column>
+            <el-table-column label="鎿嶄綔" align="center" :width="130">
+              <template #default="scope">
+                <el-row v-if="tab === 'running'" :gutter="10" class="mb8">
+                  <el-col :span="1.5">
+                    <el-popover :ref="`popoverRef${scope.$index}`" trigger="click" placement="left" :width="300">
+                      <el-input v-model="deleteReason" resize="none" :rows="3" type="textarea" placeholder="璇疯緭鍏ヤ綔搴熷師鍥�" />
+                      <div style="text-align: right; margin: 5px 0px 0px 0px">
+                        <el-button size="small" text @click="cancelPopover(scope.$index)">鍙栨秷</el-button>
+                        <el-button size="small" type="primary" @click="handleInvalid(scope.row)">纭</el-button>
+                      </div>
+                      <template #reference>
+                        <el-button link type="primary" size="small" icon="CircleClose">浣滃簾</el-button>
+                      </template>
+                    </el-popover>
+                  </el-col>
+                  <el-col :span="1.5">
+                    <el-button link type="primary" size="small" icon="Delete" @click="handleDelete(scope.row)">鍒犻櫎</el-button>
+                  </el-col>
+                </el-row>
+                <el-row :gutter="10" class="mb8">
+                  <el-col :span="1.5">
+                    <el-button link type="primary" size="small" icon="View" @click="handleView(scope.row)">鏌ョ湅</el-button>
+                  </el-col>
+                </el-row>
+              </template>
+            </el-table-column>
+          </el-table>
+          <pagination
+            v-show="total > 0"
+            v-model:page="queryParams.pageNum"
+            v-model:limit="queryParams.pageSize"
+            :total="total"
+            @pagination="handleQuery"
+          />
+        </el-card>
+      </el-col>
+    </el-row>
+    <el-dialog v-if="processDefinitionDialog.visible" v-model="processDefinitionDialog.visible" :title="processDefinitionDialog.title" width="70%">
+      <el-table v-loading="loading" :data="processDefinitionHistoryList">
+        <el-table-column fixed align="center" type="index" label="搴忓彿" width="60"></el-table-column>
+        <el-table-column fixed align="center" prop="name" label="娴佺▼瀹氫箟鍚嶇О"></el-table-column>
+        <el-table-column align="center" prop="key" label="鏍囪瘑Key"></el-table-column>
+        <el-table-column align="center" prop="version" label="鐗堟湰鍙�" width="90">
+          <template #default="scope"> v{{ scope.row.version }}.0</template>
+        </el-table-column>
+        <el-table-column align="center" prop="suspensionState" label="鐘舵��" min-width="70">
+          <template #default="scope">
+            <el-tag v-if="scope.row.suspensionState == 1" type="success">婵�娲�</el-tag>
+            <el-tag v-else type="danger">鎸傝捣</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column align="center" prop="deploymentTime" label="閮ㄧ讲鏃堕棿" :show-overflow-tooltip="true"></el-table-column>
+        <el-table-column fixed="right" label="鎿嶄綔" align="center" width="200" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-button link type="primary" size="small" icon="Sort" @click="handleChange(scope.row.id)">鍒囨崲</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </el-dialog>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import {
+  getPageByRunning,
+  getPageByFinish,
+  deleteRunAndHisInstance,
+  deleteFinishAndHisInstance,
+  deleteRunInstance
+} from '@/api/workflow/processInstance';
+import { getListByKey, migrationDefinition } from '@/api/workflow/processDefinition';
+import { listCategory } from '@/api/workflow/category';
+import { CategoryVO } from '@/api/workflow/category/types';
+import { ProcessInstanceQuery, ProcessInstanceVO } from '@/api/workflow/processInstance/types';
+import workflowCommon from '@/api/workflow/workflowCommon';
+import { RouterJumpVo } from '@/api/workflow/workflowCommon/types';
+//瀹℃壒璁板綍缁勪欢
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { wf_business_status } = toRefs<any>(proxy?.useDict('wf_business_status'));
+const queryFormRef = ref<ElFormInstance>();
+const categoryTreeRef = ref<ElTreeInstance>();
+
+// 閬僵灞�
+const loading = ref(true);
+// 閫変腑鏁扮粍
+const ids = ref<Array<any>>([]);
+// 闈炲崟涓鐢�
+const single = ref(true);
+// 闈炲涓鐢�
+const multiple = ref(true);
+// 鏄剧ず鎼滅储鏉′欢
+const showSearch = ref(true);
+// 鎬绘潯鏁�
+const total = ref(0);
+// 娴佺▼瀹氫箟id
+const processDefinitionId = ref<string>('');
+// 妯″瀷瀹氫箟琛ㄦ牸鏁版嵁
+const processInstanceList = ref<ProcessInstanceVO[]>([]);
+const processDefinitionHistoryList = ref<Array<any>>([]);
+const categoryOptions = ref<CategoryOption[]>([]);
+const categoryName = ref('');
+
+const processDefinitionDialog = reactive<DialogOption>({
+  visible: false,
+  title: '娴佺▼瀹氫箟'
+});
+
+type CategoryOption = {
+  categoryCode: string;
+  categoryName: string;
+  children?: CategoryOption[];
+};
+
+const tab = ref('running');
+// 浣滃簾鍘熷洜
+const deleteReason = ref('');
+// 鏌ヨ鍙傛暟
+const queryParams = ref<ProcessInstanceQuery>({
+  pageNum: 1,
+  pageSize: 10,
+  name: undefined,
+  key: undefined,
+  categoryCode: undefined
+});
+
+/** 鑺傜偣鍗曞嚮浜嬩欢 */
+const handleNodeClick = (data: CategoryVO) => {
+  queryParams.value.categoryCode = data.categoryCode;
+  if (data.categoryCode === 'ALL') {
+    queryParams.value.categoryCode = '';
+  }
+  handleQuery();
+};
+/** 閫氳繃鏉′欢杩囨护鑺傜偣  */
+const filterNode = (value: string, data: any) => {
+  if (!value) return true;
+  return data.categoryName.indexOf(value) !== -1;
+};
+/** 鏍规嵁鍚嶇О绛涢�夐儴闂ㄦ爲 */
+watchEffect(
+  () => {
+    categoryTreeRef.value.filter(categoryName.value);
+  },
+  {
+    flush: 'post' // watchEffect浼氬湪DOM鎸傝浇鎴栬�呮洿鏂颁箣鍓嶅氨浼氳Е鍙戯紝姝ゅ睘鎬ф帶鍒跺湪DOM鍏冪礌鏇存柊鍚庤繍琛�
+  }
+);
+
+/** 鏌ヨ娴佺▼鍒嗙被涓嬫媺鏍戠粨鏋� */
+const getTreeselect = async () => {
+  const res = await listCategory();
+  categoryOptions.value = [];
+  const data: CategoryOption = { categoryCode: 'ALL', categoryName: '椤剁骇鑺傜偣', children: [] };
+  data.children = proxy?.handleTree<CategoryOption>(res.data, 'id', 'parentId');
+  categoryOptions.value.push(data);
+};
+
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+  if ('running' === tab.value) {
+    getProcessInstanceRunningList();
+  } else {
+    getProcessInstanceFinishList();
+  }
+};
+/** 閲嶇疆鎸夐挳鎿嶄綔 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  queryParams.value.categoryCode = '';
+  queryParams.value.pageNum = 1;
+  queryParams.value.pageSize = 10;
+  handleQuery();
+};
+// 澶氶�夋閫変腑鏁版嵁
+const handleSelectionChange = (selection: ProcessInstanceVO[]) => {
+  ids.value = selection.map((item: any) => item.id);
+  single.value = selection.length !== 1;
+  multiple.value = !selection.length;
+};
+//鍒嗛〉
+const getProcessInstanceRunningList = () => {
+  loading.value = true;
+  getPageByRunning(queryParams.value).then((resp) => {
+    processInstanceList.value = resp.rows;
+    total.value = resp.total;
+    loading.value = false;
+  });
+};
+//鍒嗛〉
+const getProcessInstanceFinishList = () => {
+  loading.value = true;
+  getPageByFinish(queryParams.value).then((resp) => {
+    processInstanceList.value = resp.rows;
+    total.value = resp.total;
+    loading.value = false;
+  });
+};
+
+/** 鍒犻櫎鎸夐挳鎿嶄綔 */
+const handleDelete = async (row: any) => {
+  const id = row.id || ids.value;
+  await proxy?.$modal.confirm('鏄惁纭鍒犻櫎id涓恒��' + id + '銆戠殑鏁版嵁椤癸紵');
+  loading.value = true;
+  if ('running' === tab.value) {
+    await deleteRunAndHisInstance(id).finally(() => (loading.value = false));
+    getProcessInstanceRunningList();
+  } else {
+    await deleteFinishAndHisInstance(id).finally(() => (loading.value = false));
+    getProcessInstanceFinishList();
+  }
+  proxy?.$modal.msgSuccess('鍒犻櫎鎴愬姛');
+};
+const changeTab = async (data: string) => {
+  processInstanceList.value = [];
+  queryParams.value.pageNum = 1;
+  if ('running' === data) {
+    getProcessInstanceRunningList();
+  } else {
+    getProcessInstanceFinishList();
+  }
+};
+/** 浣滃簾鎸夐挳鎿嶄綔 */
+const handleInvalid = async (row: ProcessInstanceVO) => {
+  await proxy?.$modal.confirm('鏄惁纭浣滃簾涓氬姟id涓恒��' + row.businessKey + '銆戠殑鏁版嵁椤癸紵');
+  loading.value = true;
+  if ('running' === tab.value) {
+    let param = {
+      processInstanceId: row.id,
+      deleteReason: deleteReason.value
+    };
+    await deleteRunInstance(param).finally(() => (loading.value = false));
+    getProcessInstanceRunningList();
+    proxy?.$modal.msgSuccess('鎿嶄綔鎴愬姛');
+  }
+};
+const cancelPopover = async (index: any) => {
+  (proxy?.$refs[`popoverRef${index}`] as any).hide(); //鍏抽棴寮圭獥
+};
+//鑾峰彇娴佺▼瀹氫箟
+const getProcessDefinitionHitoryList = (id: string, key: string) => {
+  processDefinitionDialog.visible = true;
+  processDefinitionId.value = id;
+  loading.value = true;
+  getListByKey(key).then((resp) => {
+    if (resp.data && resp.data.length > 0) {
+      processDefinitionHistoryList.value = resp.data.filter((item: any) => item.id !== id);
+    }
+    loading.value = false;
+  });
+};
+//鍒囨崲娴佺▼鐗堟湰
+const handleChange = async (id: string) => {
+  await proxy?.$modal.confirm('鏄惁纭鍒囨崲锛�');
+  loading.value = true;
+  migrationDefinition(processDefinitionId.value, id).then((resp) => {
+    proxy?.$modal.msgSuccess('鎿嶄綔鎴愬姛');
+    getProcessInstanceRunningList();
+    processDefinitionDialog.visible = false;
+    loading.value = false;
+  });
+};
+/** 鏌ョ湅鎸夐挳鎿嶄綔 */
+const handleView = (row) => {
+  const routerJumpVo = reactive<RouterJumpVo>({
+    wfDefinitionConfigVo: row.wfDefinitionConfigVo,
+    wfNodeConfigVo: row.wfNodeConfigVo,
+    businessKey: row.businessKey,
+    taskId: row.id,
+    type: 'view'
+  });
+  workflowCommon.routerJump(routerJumpVo, proxy);
+};
+
+onMounted(() => {
+  getProcessInstanceRunningList();
+  getTreeselect();
+});
+</script>
diff --git a/src/views/workflow/task/allTaskWaiting.vue b/src/views/workflow/task/allTaskWaiting.vue
new file mode 100644
index 0000000..fe530b8
--- /dev/null
+++ b/src/views/workflow/task/allTaskWaiting.vue
@@ -0,0 +1,286 @@
+<template>
+  <div class="p-2">
+    <div class="mb-[10px]">
+      <el-card shadow="hover" class="text-center">
+        <el-radio-group v-model="tab" @change="changeTab(tab)">
+          <el-radio-button value="waiting">寰呭姙浠诲姟</el-radio-button>
+          <el-radio-button value="finish">宸插姙浠诲姟</el-radio-button>
+        </el-radio-group>
+      </el-card>
+    </div>
+    <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+      <div v-show="showSearch" class="mb-[10px]">
+        <el-card shadow="hover">
+          <el-form v-show="showSearch" ref="queryFormRef" :model="queryParams" :inline="true">
+            <el-form-item label="浠诲姟鍚嶇О" prop="name">
+              <el-input v-model="queryParams.name" placeholder="璇疯緭鍏ヤ换鍔″悕绉�" @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="娴佺▼瀹氫箟鍚嶇О" label-width="100" prop="processDefinitionName">
+              <el-input v-model="queryParams.processDefinitionName" placeholder="璇疯緭鍏ユ祦绋嬪畾涔夊悕绉�" @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="娴佺▼瀹氫箟KEY" label-width="100" prop="processDefinitionKey">
+              <el-input v-model="queryParams.processDefinitionKey" placeholder="璇疯緭鍏ユ祦绋嬪畾涔塊EY" @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>
+        </el-card>
+      </div>
+    </transition>
+    <el-card shadow="hover">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+          <el-col :span="1.5">
+            <el-button type="primary" plain icon="Edit" :disabled="multiple" @click="handleUpdate">淇敼鍔炵悊浜�</el-button>
+          </el-col>
+          <right-toolbar v-model:showSearch="showSearch" @query-table="handleQuery"></right-toolbar>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" border :data="taskList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column align="center" type="index" label="搴忓彿" width="60"></el-table-column>
+        <el-table-column :show-overflow-tooltip="true" align="center" label="娴佺▼瀹氫箟鍚嶇О">
+          <template #default="scope">
+            <span>{{ scope.row.processDefinitionName }}v{{ scope.row.processDefinitionVersion }}.0</span>
+          </template>
+        </el-table-column>
+        <el-table-column align="center" prop="processDefinitionKey" label="娴佺▼瀹氫箟KEY"></el-table-column>
+        <el-table-column align="center" prop="name" label="浠诲姟鍚嶇О"></el-table-column>
+        <el-table-column align="center" prop="assigneeName" label="鍔炵悊浜�">
+          <template v-if="tab === 'waiting'" #default="scope">
+            <template v-if="scope.row.participantVo && scope.row.assignee === null">
+              <el-tag v-for="(item, index) in scope.row.participantVo.candidateName" :key="index" type="success">
+                {{ item }}
+              </el-tag>
+            </template>
+            <template v-else>
+              <el-tag type="success">
+                {{ scope.row.assigneeName || '鏃�' }}
+              </el-tag>
+            </template>
+          </template>
+          <template v-else-if="tab === 'finish'" #default="scope">
+            <el-tag type="success">
+              {{ scope.row.assigneeName || '鏃�' }}
+            </el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column align="center" label="娴佺▼鐘舵��" min-width="70">
+          <template #default="scope">
+            <dict-tag v-if="tab === 'waiting'" :options="wf_business_status" :value="scope.row.businessStatus"></dict-tag>
+            <el-tag v-else type="success">宸插畬鎴�</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column v-if="tab === 'waiting'" align="center" prop="createTime" label="鍒涘缓鏃堕棿" width="160"></el-table-column>
+        <el-table-column v-if="tab === 'finish'" align="center" prop="startTime" label="鍒涘缓鏃堕棿" width="160"></el-table-column>
+        <el-table-column label="鎿嶄綔" align="center" :width="tab === 'finish' ? '80' : '151'">
+          <template #default="scope">
+            <el-row :gutter="10" class="mb8">
+              <el-col :span="1.5">
+                <el-button link type="primary" size="small" icon="View" @click="handleView(scope.row)">鏌ョ湅</el-button>
+              </el-col>
+              <el-col v-if="tab === 'waiting'" :span="1.5">
+                <el-button link type="primary" size="small" icon="Document" @click="handleInstanceVariable(scope.row)">娴佺▼鍙橀噺</el-button>
+              </el-col>
+            </el-row>
+            <el-row v-if="scope.row.multiInstance" :gutter="10" class="mb8">
+              <el-col :span="1.5">
+                <el-button link type="primary" size="small" icon="Remove" @click="deleteMultiInstanceUser(scope.row)">鍑忕</el-button>
+              </el-col>
+              <el-col :span="1.5">
+                <el-button link type="primary" size="small" icon="CirclePlus" @click="addMultiInstanceUser(scope.row)">鍔犵</el-button>
+              </el-col>
+            </el-row>
+          </template>
+        </el-table-column>
+      </el-table>
+      <pagination
+        v-show="total > 0"
+        v-model:page="queryParams.pageNum"
+        v-model:limit="queryParams.pageSize"
+        :total="total"
+        @pagination="handleQuery"
+      />
+    </el-card>
+    <!-- 鍔犵缁勪欢 -->
+    <multiInstanceUser ref="multiInstanceUserRef" :title="title" @submit-callback="handleQuery" />
+    <!-- 閫変汉缁勪欢 -->
+    <UserSelect ref="userSelectRef" :multiple="false" @confirm-call-back="submitCallback"></UserSelect>
+    <!-- 娴佺▼鍙橀噺寮�濮� -->
+    <el-dialog v-model="variableVisible" draggable title="娴佺▼鍙橀噺" width="60%" :close-on-click-modal="false">
+      <el-card v-loading="variableLoading" class="box-card">
+        <template #header>
+          <div class="clearfix">
+            <span
+              >娴佺▼瀹氫箟鍚嶇О锛�<el-tag>{{ processDefinitionName }}</el-tag></span
+            >
+          </div>
+        </template>
+        <div v-for="(v, index) in variableList" :key="index">
+          <el-form v-if="v.key !== '_FLOWABLE_SKIP_EXPRESSION_ENABLED'" :label-position="'right'" label-width="150px">
+            <el-form-item :label="v.key + '锛�'">
+              {{ v.value }}
+            </el-form-item>
+          </el-form>
+        </div>
+      </el-card>
+    </el-dialog>
+    <!-- 娴佺▼鍙橀噺缁撴潫 -->
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { getPageByAllTaskWait, getPageByAllTaskFinish, updateAssignee, getInstanceVariable } from '@/api/workflow/task';
+import MultiInstanceUser from '@/components/Process/multiInstanceUser.vue';
+import UserSelect from '@/components/UserSelect';
+import { TaskQuery, TaskVO, VariableVo } from '@/api/workflow/task/types';
+import workflowCommon from '@/api/workflow/workflowCommon';
+import { RouterJumpVo } from '@/api/workflow/workflowCommon/types';
+//瀹℃壒璁板綍缁勪欢
+//鍔犵缁勪欢
+const multiInstanceUserRef = ref<InstanceType<typeof MultiInstanceUser>>();
+//閫変汉缁勪欢
+const userSelectRef = ref<InstanceType<typeof UserSelect>>();
+
+const queryFormRef = ref<ElFormInstance>();
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { wf_business_status } = toRefs<any>(proxy?.useDict('wf_business_status'));
+// 閬僵灞�
+const loading = ref(true);
+// 閫変腑鏁扮粍
+const ids = ref<Array<any>>([]);
+// 闈炲崟涓鐢�
+const single = ref(true);
+// 闈炲涓鐢�
+const multiple = ref(true);
+// 鏄剧ず鎼滅储鏉′欢
+const showSearch = ref(true);
+// 鎬绘潯鏁�
+const total = ref(0);
+// 妯″瀷瀹氫箟琛ㄦ牸鏁版嵁
+const taskList = ref([]);
+const title = ref('');
+// 娴佺▼鍙橀噺鏄惁鏄剧ず
+const variableVisible = ref(false);
+const variableLoading = ref(true);
+// 娴佺▼鍙橀噺
+const variableList = ref<VariableVo>({
+  key: '',
+  value: ''
+});
+//娴佺▼瀹氫箟鍚嶇О
+const processDefinitionName = ref();
+// 鏌ヨ鍙傛暟
+const queryParams = ref<TaskQuery>({
+  pageNum: 1,
+  pageSize: 10,
+  name: undefined,
+  processDefinitionName: undefined,
+  processDefinitionKey: undefined
+});
+const tab = ref('waiting');
+
+//鍔犵
+const addMultiInstanceUser = (row: TaskVO) => {
+  if (multiInstanceUserRef.value) {
+    title.value = '鍔犵浜哄憳';
+    multiInstanceUserRef.value.getAddMultiInstanceList(row.id, []);
+  }
+};
+//鍑忕
+const deleteMultiInstanceUser = (row: TaskVO) => {
+  if (multiInstanceUserRef.value) {
+    title.value = '鍑忕浜哄憳';
+    multiInstanceUserRef.value.getDeleteMultiInstanceList(row.id);
+  }
+};
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+  if ('waiting' === tab.value) {
+    getWaitingList();
+  } else {
+    getFinishList();
+  }
+};
+/** 閲嶇疆鎸夐挳鎿嶄綔 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  queryParams.value.pageNum = 1;
+  queryParams.value.pageSize = 10;
+  handleQuery();
+};
+// 澶氶�夋閫変腑鏁版嵁
+const handleSelectionChange = (selection: any) => {
+  ids.value = selection.map((item: any) => item.id);
+  single.value = selection.length !== 1;
+  multiple.value = !selection.length;
+};
+const changeTab = async (data: string) => {
+  taskList.value = [];
+  queryParams.value.pageNum = 1;
+  if ('waiting' === data) {
+    getWaitingList();
+  } else {
+    getFinishList();
+  }
+};
+//鍒嗛〉
+const getWaitingList = () => {
+  loading.value = true;
+  getPageByAllTaskWait(queryParams.value).then((resp) => {
+    taskList.value = resp.rows;
+    total.value = resp.total;
+    loading.value = false;
+  });
+};
+const getFinishList = () => {
+  loading.value = true;
+  getPageByAllTaskFinish(queryParams.value).then((resp) => {
+    taskList.value = resp.rows;
+    total.value = resp.total;
+    loading.value = false;
+  });
+};
+//鎵撳紑淇敼閫変汉
+const handleUpdate = () => {
+  userSelectRef.value.open();
+};
+//淇敼鍔炵悊浜�
+const submitCallback = async (data) => {
+  if (data && data.length > 0) {
+    await proxy?.$modal.confirm('鏄惁纭鎻愪氦锛�');
+    loading.value = true;
+    await updateAssignee(ids.value, data[0].userId);
+    handleQuery();
+    proxy?.$modal.msgSuccess('鎿嶄綔鎴愬姛');
+  } else {
+    proxy?.$modal.msgWarning('璇烽�夋嫨鐢ㄦ埛锛�');
+  }
+};
+//鏌ヨ娴佺▼鍙橀噺
+const handleInstanceVariable = async (row: TaskVO) => {
+  variableLoading.value = true;
+  variableVisible.value = true;
+  processDefinitionName.value = row.processDefinitionName;
+  let data = await getInstanceVariable(row.id);
+  variableList.value = data.data;
+  variableLoading.value = false;
+};
+/** 鏌ョ湅鎸夐挳鎿嶄綔 */
+const handleView = (row) => {
+  const routerJumpVo = reactive<RouterJumpVo>({
+    wfDefinitionConfigVo: row.wfDefinitionConfigVo,
+    wfNodeConfigVo: row.wfNodeConfigVo,
+    businessKey: row.businessKey,
+    taskId: row.id,
+    type: 'view'
+  });
+  workflowCommon.routerJump(routerJumpVo, proxy);
+};
+onMounted(() => {
+  getWaitingList();
+});
+</script>
diff --git a/src/views/workflow/task/myDocument.vue b/src/views/workflow/task/myDocument.vue
new file mode 100644
index 0000000..e8592f8
--- /dev/null
+++ b/src/views/workflow/task/myDocument.vue
@@ -0,0 +1,261 @@
+<template>
+  <div class="p-2">
+    <el-row :gutter="20">
+      <!-- 娴佺▼鍒嗙被鏍� -->
+      <el-col :lg="4" :xs="24" style="">
+        <el-card shadow="hover">
+          <el-input v-model="categoryName" placeholder="璇疯緭鍏ユ祦绋嬪垎绫诲悕" prefix-icon="Search" clearable />
+          <el-tree
+            ref="categoryTreeRef"
+            class="mt-2"
+            node-key="id"
+            :data="categoryOptions"
+            :props="{ label: 'categoryName', children: 'children' }"
+            :expand-on-click-node="false"
+            :filter-node-method="filterNode"
+            highlight-current
+            default-expand-all
+            @node-click="handleNodeClick"
+          ></el-tree>
+        </el-card>
+      </el-col>
+      <el-col :lg="20" :xs="24">
+        <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+          <div v-show="showSearch" class="mb-[10px]">
+            <el-card shadow="hover">
+              <el-form v-show="showSearch" ref="queryFormRef" :model="queryParams" :inline="true" label-width="120px">
+                <el-form-item label="娴佺▼瀹氫箟鍚嶇О" prop="name">
+                  <el-input v-model="queryParams.name" placeholder="璇疯緭鍏ユ祦绋嬪畾涔夊悕绉�" @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>
+            </el-card>
+          </div>
+        </transition>
+        <el-card shadow="hover">
+          <template #header>
+            <el-row :gutter="10" class="mb8">
+              <right-toolbar v-model:showSearch="showSearch" @query-table="handleQuery"></right-toolbar>
+            </el-row>
+          </template>
+
+          <el-table v-loading="loading" border :data="processInstanceList" @selection-change="handleSelectionChange">
+            <el-table-column type="selection" width="55" align="center" />
+            <el-table-column align="center" type="index" label="搴忓彿" width="60"></el-table-column>
+            <el-table-column v-if="false" align="center" prop="id" label="id"></el-table-column>
+            <el-table-column :show-overflow-tooltip="true" align="center" label="娴佺▼瀹氫箟鍚嶇О">
+              <template #default="scope">
+                <span>{{ scope.row.processDefinitionName }}v{{ scope.row.processDefinitionVersion }}.0</span>
+              </template>
+            </el-table-column>
+            <el-table-column align="center" prop="processDefinitionKey" label="娴佺▼瀹氫箟KEY"></el-table-column>
+            <el-table-column align="center" prop="processDefinitionVersion" label="鐗堟湰鍙�" width="90">
+              <template #default="scope"> v{{ scope.row.processDefinitionVersion }}.0</template>
+            </el-table-column>
+            <el-table-column v-if="tab === 'running'" align="center" prop="isSuspended" label="鐘舵��" min-width="70">
+              <template #default="scope">
+                <el-tag v-if="!scope.row.isSuspended" type="success">婵�娲�</el-tag>
+                <el-tag v-else type="danger">鎸傝捣</el-tag>
+              </template>
+            </el-table-column>
+            <el-table-column align="center" label="娴佺▼鐘舵��" min-width="70">
+              <template #default="scope">
+                <dict-tag :options="wf_business_status" :value="scope.row.businessStatus"></dict-tag>
+              </template>
+            </el-table-column>
+            <el-table-column align="center" prop="startTime" label="鍚姩鏃堕棿" width="160"></el-table-column>
+            <el-table-column v-if="tab === 'finish'" align="center" prop="endTime" label="缁撴潫鏃堕棿" width="160"></el-table-column>
+            <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width">
+              <template #default="scope">
+                <el-tooltip
+                  v-if="scope.row.businessStatus === 'draft' || scope.row.businessStatus === 'cancel' || scope.row.businessStatus === 'back'"
+                  content="淇敼"
+                  placement="top"
+                >
+                  <el-button v-hasPermi="['demo:leave:edit']" link type="primary" icon="Edit" @click="handleOpen(scope.row, 'update')"></el-button>
+                </el-tooltip>
+                <el-tooltip
+                  v-if="scope.row.businessStatus === 'draft' || scope.row.businessStatus === 'cancel' || scope.row.businessStatus === 'back'"
+                  content="鍒犻櫎"
+                  placement="top"
+                >
+                  <el-button v-hasPermi="['demo:leave:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
+                </el-tooltip>
+                <el-tooltip placement="top" content="鏌ョ湅">
+                  <el-button link type="primary" icon="View" @click="handleOpen(scope.row, 'view')"></el-button>
+                </el-tooltip>
+                <el-tooltip v-if="scope.row.businessStatus === 'waiting'" content="鎾ら攢" placement="top">
+                  <el-button link type="primary" icon="Notification" @click="handleCancelProcessApply(scope.row.id)"></el-button>
+                </el-tooltip>
+              </template>
+            </el-table-column>
+          </el-table>
+          <pagination
+            v-show="total > 0"
+            v-model:page="queryParams.pageNum"
+            v-model:limit="queryParams.pageSize"
+            :total="total"
+            @pagination="getList"
+          />
+        </el-card>
+      </el-col>
+    </el-row>
+    <!-- 鎻愪氦缁勪欢 -->
+    <submitVerify ref="submitVerifyRef" @submit-callback="getList" />
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { getPageByCurrent, deleteRunAndHisInstance, cancelProcessApply } from '@/api/workflow/processInstance';
+import { listCategory } from '@/api/workflow/category';
+import { CategoryVO } from '@/api/workflow/category/types';
+import { ProcessInstanceQuery, ProcessInstanceVO } from '@/api/workflow/processInstance/types';
+import workflowCommon from '@/api/workflow/workflowCommon';
+import { RouterJumpVo } from '@/api/workflow/workflowCommon/types';
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { wf_business_status } = toRefs<any>(proxy?.useDict('wf_business_status'));
+const queryFormRef = ref<ElFormInstance>();
+const categoryTreeRef = ref<ElTreeInstance>();
+
+// 閬僵灞�
+const loading = ref(true);
+// 閫変腑鏁扮粍
+const ids = ref<Array<any>>([]);
+// 闈炲崟涓鐢�
+const single = ref(true);
+// 闈炲涓鐢�
+const multiple = ref(true);
+// 鏄剧ず鎼滅储鏉′欢
+const showSearch = ref(true);
+// 鎬绘潯鏁�
+const total = ref(0);
+// 妯″瀷瀹氫箟琛ㄦ牸鏁版嵁
+const processInstanceList = ref<ProcessInstanceVO[]>([]);
+
+const categoryOptions = ref<CategoryOption[]>([]);
+const categoryName = ref('');
+
+interface CategoryOption {
+  categoryCode: string;
+  categoryName: string;
+  children?: CategoryOption[];
+}
+
+const tab = ref('running');
+// 鏌ヨ鍙傛暟
+const queryParams = ref<ProcessInstanceQuery>({
+  pageNum: 1,
+  pageSize: 10,
+  name: undefined,
+  categoryCode: undefined
+});
+
+onMounted(() => {
+  getList();
+  getTreeselect();
+});
+
+/** 鑺傜偣鍗曞嚮浜嬩欢 */
+const handleNodeClick = (data: CategoryVO) => {
+  queryParams.value.categoryCode = data.categoryCode;
+  if (data.categoryCode === 'ALL') {
+    queryParams.value.categoryCode = '';
+  }
+  handleQuery();
+};
+/** 閫氳繃鏉′欢杩囨护鑺傜偣  */
+const filterNode = (value: string, data: any) => {
+  if (!value) return true;
+  return data.categoryName.indexOf(value) !== -1;
+};
+/** 鏍规嵁鍚嶇О绛涢�夐儴闂ㄦ爲 */
+watchEffect(
+  () => {
+    categoryTreeRef.value.filter(categoryName.value);
+  },
+  {
+    flush: 'post' // watchEffect浼氬湪DOM鎸傝浇鎴栬�呮洿鏂颁箣鍓嶅氨浼氳Е鍙戯紝姝ゅ睘鎬ф帶鍒跺湪DOM鍏冪礌鏇存柊鍚庤繍琛�
+  }
+);
+
+/** 鏌ヨ娴佺▼鍒嗙被涓嬫媺鏍戠粨鏋� */
+const getTreeselect = async () => {
+  const res = await listCategory();
+  categoryOptions.value = [];
+  const data: CategoryOption = { categoryCode: 'ALL', categoryName: '椤剁骇鑺傜偣', children: [] };
+  data.children = proxy?.handleTree<CategoryOption>(res.data, 'id', 'parentId');
+  categoryOptions.value.push(data);
+};
+
+//瀹℃壒璁板綍
+const handleApprovalRecord = (processInstanceId: string) => {
+  if (approvalRecordRef.value) {
+    approvalRecordRef.value.init(processInstanceId);
+  }
+};
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+  getList();
+};
+/** 閲嶇疆鎸夐挳鎿嶄綔 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  queryParams.value.categoryCode = '';
+  queryParams.value.pageNum = 1;
+  queryParams.value.pageSize = 10;
+  handleQuery();
+};
+// 澶氶�夋閫変腑鏁版嵁
+const handleSelectionChange = (selection: ProcessInstanceVO[]) => {
+  ids.value = selection.map((item: any) => item.id);
+  single.value = selection.length !== 1;
+  multiple.value = !selection.length;
+};
+//鍒嗛〉
+const getList = () => {
+  loading.value = true;
+  getPageByCurrent(queryParams.value).then((resp) => {
+    processInstanceList.value = resp.rows;
+    total.value = resp.total;
+    loading.value = false;
+  });
+};
+
+/** 鍒犻櫎鎸夐挳鎿嶄綔 */
+const handleDelete = async (row: ProcessInstanceVO) => {
+  const id = row.id || ids.value;
+  await proxy?.$modal.confirm('鏄惁纭鍒犻櫎id涓恒��' + id + '銆戠殑鏁版嵁椤癸紵');
+  loading.value = true;
+  if ('running' === tab.value) {
+    await deleteRunAndHisInstance(id).finally(() => (loading.value = false));
+    getList();
+  }
+  proxy?.$modal.msgSuccess('鍒犻櫎鎴愬姛');
+};
+
+/** 鎾ら攢鎸夐挳鎿嶄綔 */
+const handleCancelProcessApply = async (processInstanceId: string) => {
+  await proxy?.$modal.confirm('鏄惁纭鎾ら攢褰撳墠鍗曟嵁锛�');
+  loading.value = true;
+  if ('running' === tab.value) {
+    await cancelProcessApply(processInstanceId).finally(() => (loading.value = false));
+    getList();
+  }
+  proxy?.$modal.msgSuccess('鎾ら攢鎴愬姛');
+};
+
+//鍔炵悊
+const handleOpen = async (row, type) => {
+  const routerJumpVo = reactive<RouterJumpVo>({
+    wfDefinitionConfigVo: row.wfDefinitionConfigVo,
+    wfNodeConfigVo: row.wfNodeConfigVo,
+    businessKey: row.businessKey,
+    taskId: row.id,
+    type: type
+  });
+  workflowCommon.routerJump(routerJumpVo, proxy);
+};
+</script>
diff --git a/src/views/workflow/task/taskCopyList.vue b/src/views/workflow/task/taskCopyList.vue
new file mode 100644
index 0000000..7784a08
--- /dev/null
+++ b/src/views/workflow/task/taskCopyList.vue
@@ -0,0 +1,150 @@
+<template>
+  <div class="p-2">
+    <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+      <div v-show="showSearch" class="mb-[10px]">
+        <el-card shadow="hover">
+          <el-form v-show="showSearch" ref="queryFormRef" :model="queryParams" :inline="true">
+            <el-form-item label="浠诲姟鍚嶇О" prop="name">
+              <el-input v-model="queryParams.name" placeholder="璇疯緭鍏ヤ换鍔″悕绉�" @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="娴佺▼瀹氫箟鍚嶇О" label-width="100" prop="processDefinitionName">
+              <el-input v-model="queryParams.processDefinitionName" placeholder="璇疯緭鍏ユ祦绋嬪畾涔夊悕绉�" @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="娴佺▼瀹氫箟KEY" label-width="100" prop="processDefinitionKey">
+              <el-input v-model="queryParams.processDefinitionKey" placeholder="璇疯緭鍏ユ祦绋嬪畾涔塊EY" @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>
+        </el-card>
+      </div>
+    </transition>
+    <el-card shadow="hover">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+          <right-toolbar v-model:showSearch="showSearch" @query-table="handleQuery"></right-toolbar>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" border :data="taskList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column align="center" type="index" label="搴忓彿" width="60"></el-table-column>
+        <el-table-column :show-overflow-tooltip="true" align="center" label="娴佺▼瀹氫箟鍚嶇О">
+          <template #default="scope">
+            <span>{{ scope.row.processDefinitionName }}v{{ scope.row.processDefinitionVersion }}.0</span>
+          </template>
+        </el-table-column>
+        <el-table-column align="center" prop="processDefinitionKey" label="娴佺▼瀹氫箟KEY"></el-table-column>
+        <el-table-column align="center" prop="name" label="浠诲姟鍚嶇О"></el-table-column>
+        <el-table-column align="center" prop="assigneeName" label="鍔炵悊浜�">
+          <template #default="scope">
+            <template v-if="scope.row.participantVo && scope.row.assignee === null">
+              <el-tag v-for="(item, index) in scope.row.participantVo.candidateName" :key="index" type="success">
+                {{ item }}
+              </el-tag>
+            </template>
+            <template v-else>
+              <el-tag type="success">
+                {{ scope.row.assigneeName || '鏃�' }}
+              </el-tag>
+            </template>
+          </template>
+        </el-table-column>
+        <el-table-column align="center" label="娴佺▼鐘舵��" min-width="70">
+          <template #default="scope">
+            <dict-tag :options="wf_business_status" :value="scope.row.businessStatus"></dict-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="鎿嶄綔" align="center" width="200">
+          <template #default="scope">
+            <el-button type="primary" size="small" icon="View" @click="handleView(scope.row)">鏌ョ湅</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <pagination
+        v-show="total > 0"
+        v-model:page="queryParams.pageNum"
+        v-model:limit="queryParams.pageSize"
+        :total="total"
+        @pagination="handleQuery"
+      />
+    </el-card>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { getPageByTaskCopy } from '@/api/workflow/task';
+import { TaskQuery } from '@/api/workflow/task/types';
+import workflowCommon from '@/api/workflow/workflowCommon';
+import { RouterJumpVo } from '@/api/workflow/workflowCommon/types';
+//瀹℃壒璁板綍缁勪欢
+const queryFormRef = ref<ElFormInstance>();
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { wf_business_status } = toRefs<any>(proxy?.useDict('wf_business_status'));
+// 閬僵灞�
+const loading = ref(true);
+// 閫変腑鏁扮粍
+const ids = ref<Array<any>>([]);
+// 闈炲崟涓鐢�
+const single = ref(true);
+// 闈炲涓鐢�
+const multiple = ref(true);
+// 鏄剧ず鎼滅储鏉′欢
+const showSearch = ref(true);
+// 鎬绘潯鏁�
+const total = ref(0);
+// 妯″瀷瀹氫箟琛ㄦ牸鏁版嵁
+const taskList = ref([]);
+// 鏌ヨ鍙傛暟
+const queryParams = ref<TaskQuery>({
+  pageNum: 1,
+  pageSize: 10,
+  name: undefined,
+  processDefinitionName: undefined,
+  processDefinitionKey: undefined
+});
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+  getTaskCopyList();
+};
+/** 閲嶇疆鎸夐挳鎿嶄綔 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  queryParams.value.pageNum = 1;
+  queryParams.value.pageSize = 10;
+  handleQuery();
+};
+// 澶氶�夋閫変腑鏁版嵁
+const handleSelectionChange = (selection: any) => {
+  ids.value = selection.map((item: any) => item.id);
+  single.value = selection.length !== 1;
+  multiple.value = !selection.length;
+};
+//鍒嗛〉
+const getTaskCopyList = () => {
+  loading.value = true;
+  getPageByTaskCopy(queryParams.value).then((resp) => {
+    taskList.value = resp.rows;
+    total.value = resp.total;
+    loading.value = false;
+  });
+};
+
+/** 鏌ョ湅鎸夐挳鎿嶄綔 */
+const handleView = (row) => {
+  const routerJumpVo = reactive<RouterJumpVo>({
+    wfDefinitionConfigVo: row.wfDefinitionConfigVo,
+    wfNodeConfigVo: row.wfNodeConfigVo,
+    businessKey: row.businessKey,
+    taskId: row.id,
+    type: 'view'
+  });
+  workflowCommon.routerJump(routerJumpVo, proxy);
+};
+
+onMounted(() => {
+  getTaskCopyList();
+});
+</script>
diff --git a/src/views/workflow/task/taskFinish.vue b/src/views/workflow/task/taskFinish.vue
new file mode 100644
index 0000000..155bd4f
--- /dev/null
+++ b/src/views/workflow/task/taskFinish.vue
@@ -0,0 +1,136 @@
+<template>
+  <div class="p-2">
+    <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+      <div v-show="showSearch" class="mb-[10px]">
+        <el-card shadow="hover">
+          <el-form v-show="showSearch" ref="queryFormRef" :model="queryParams" :inline="true">
+            <el-form-item label="浠诲姟鍚嶇О" prop="name">
+              <el-input v-model="queryParams.name" placeholder="璇疯緭鍏ヤ换鍔″悕绉�" @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="娴佺▼瀹氫箟鍚嶇О" label-width="100" prop="processDefinitionName">
+              <el-input v-model="queryParams.processDefinitionName" placeholder="璇疯緭鍏ユ祦绋嬪畾涔夊悕绉�" @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="娴佺▼瀹氫箟KEY" label-width="100" prop="processDefinitionKey">
+              <el-input v-model="queryParams.processDefinitionKey" placeholder="璇疯緭鍏ユ祦绋嬪畾涔塊EY" @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>
+        </el-card>
+      </div>
+    </transition>
+    <el-card shadow="hover">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+          <right-toolbar v-model:showSearch="showSearch" @query-table="handleQuery"></right-toolbar>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" border :data="taskList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column align="center" type="index" label="搴忓彿" width="60"></el-table-column>
+        <el-table-column :show-overflow-tooltip="true" align="center" label="娴佺▼瀹氫箟鍚嶇О">
+          <template #default="scope">
+            <span>{{ scope.row.processDefinitionName }}v{{ scope.row.processDefinitionVersion }}.0</span>
+          </template>
+        </el-table-column>
+        <el-table-column align="center" prop="processDefinitionKey" label="娴佺▼瀹氫箟KEY"></el-table-column>
+        <el-table-column align="center" prop="name" label="浠诲姟鍚嶇О"></el-table-column>
+        <el-table-column align="center" prop="assigneeName" label="鍔炵悊浜�">
+          <template #default="scope">
+            <el-tag type="success">
+              {{ scope.row.assigneeName || '鏃�' }}
+            </el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column align="center" prop="startTime" label="鍒涘缓鏃堕棿" width="160"></el-table-column>
+        <el-table-column label="鎿嶄綔" align="center" width="200">
+          <template #default="scope">
+            <el-button type="primary" size="small" icon="View" @click="handleView(scope.row)">鏌ョ湅</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <pagination
+        v-show="total > 0"
+        v-model:page="queryParams.pageNum"
+        v-model:limit="queryParams.pageSize"
+        :total="total"
+        @pagination="handleQuery"
+      />
+    </el-card>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { getPageByTaskFinish } from '@/api/workflow/task';
+import { TaskQuery, TaskVO } from '@/api/workflow/task/types';
+import workflowCommon from '@/api/workflow/workflowCommon';
+import { RouterJumpVo } from '@/api/workflow/workflowCommon/types';
+//瀹℃壒璁板綍缁勪欢
+const queryFormRef = ref<ElFormInstance>();
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+// 閬僵灞�
+const loading = ref(true);
+// 閫変腑鏁扮粍
+const ids = ref<Array<any>>([]);
+// 闈炲崟涓鐢�
+const single = ref(true);
+// 闈炲涓鐢�
+const multiple = ref(true);
+// 鏄剧ず鎼滅储鏉′欢
+const showSearch = ref(true);
+// 鎬绘潯鏁�
+const total = ref(0);
+// 妯″瀷瀹氫箟琛ㄦ牸鏁版嵁
+const taskList = ref([]);
+// 鏌ヨ鍙傛暟
+const queryParams = ref<TaskQuery>({
+  pageNum: 1,
+  pageSize: 10,
+  name: undefined,
+  processDefinitionName: undefined,
+  processDefinitionKey: undefined
+});
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+  getFinishList();
+};
+/** 閲嶇疆鎸夐挳鎿嶄綔 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  queryParams.value.pageNum = 1;
+  queryParams.value.pageSize = 10;
+  handleQuery();
+};
+// 澶氶�夋閫変腑鏁版嵁
+const handleSelectionChange = (selection: any) => {
+  ids.value = selection.map((item: any) => item.id);
+  single.value = selection.length !== 1;
+  multiple.value = !selection.length;
+};
+const getFinishList = () => {
+  loading.value = true;
+  getPageByTaskFinish(queryParams.value).then((resp) => {
+    taskList.value = resp.rows;
+    total.value = resp.total;
+    loading.value = false;
+  });
+};
+/** 鏌ョ湅鎸夐挳鎿嶄綔 */
+const handleView = (row: TaskVO) => {
+  const routerJumpVo = reactive<RouterJumpVo>({
+    wfDefinitionConfigVo: row.wfDefinitionConfigVo,
+    wfNodeConfigVo: row.wfNodeConfigVo,
+    businessKey: row.businessKey,
+    taskId: row.id,
+    type: 'view'
+  });
+  workflowCommon.routerJump(routerJumpVo, proxy);
+};
+
+onMounted(() => {
+  getFinishList();
+});
+</script>
diff --git a/src/views/workflow/task/taskWaiting.vue b/src/views/workflow/task/taskWaiting.vue
new file mode 100644
index 0000000..dc6546a
--- /dev/null
+++ b/src/views/workflow/task/taskWaiting.vue
@@ -0,0 +1,149 @@
+<template>
+  <div class="p-2">
+    <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+      <div v-show="showSearch" class="mb-[10px]">
+        <el-card shadow="hover">
+          <el-form v-show="showSearch" ref="queryFormRef" :model="queryParams" :inline="true">
+            <el-form-item label="浠诲姟鍚嶇О" prop="name">
+              <el-input v-model="queryParams.name" placeholder="璇疯緭鍏ヤ换鍔″悕绉�" @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="娴佺▼瀹氫箟鍚嶇О" label-width="100" prop="processDefinitionName">
+              <el-input v-model="queryParams.processDefinitionName" placeholder="璇疯緭鍏ユ祦绋嬪畾涔夊悕绉�" @keyup.enter="handleQuery" />
+            </el-form-item>
+            <el-form-item label="娴佺▼瀹氫箟KEY" label-width="100" prop="processDefinitionKey">
+              <el-input v-model="queryParams.processDefinitionKey" placeholder="璇疯緭鍏ユ祦绋嬪畾涔塊EY" @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>
+        </el-card>
+      </div>
+    </transition>
+    <el-card shadow="hover">
+      <template #header>
+        <el-row :gutter="10" class="mb8">
+          <right-toolbar v-model:showSearch="showSearch" @query-table="handleQuery"></right-toolbar>
+        </el-row>
+      </template>
+
+      <el-table v-loading="loading" border :data="taskList" @selection-change="handleSelectionChange">
+        <el-table-column type="selection" width="55" align="center" />
+        <el-table-column align="center" type="index" label="搴忓彿" width="60"></el-table-column>
+        <el-table-column :show-overflow-tooltip="true" align="center" label="娴佺▼瀹氫箟鍚嶇О">
+          <template #default="scope">
+            <span>{{ scope.row.processDefinitionName }}v{{ scope.row.processDefinitionVersion }}.0</span>
+          </template>
+        </el-table-column>
+        <el-table-column align="center" prop="processDefinitionKey" label="娴佺▼瀹氫箟KEY"></el-table-column>
+        <el-table-column align="center" prop="name" label="浠诲姟鍚嶇О"></el-table-column>
+        <el-table-column align="center" prop="assigneeName" label="鍔炵悊浜�">
+          <template #default="scope">
+            <template v-if="scope.row.participantVo && scope.row.assignee === null">
+              <el-tag v-for="(item, index) in scope.row.participantVo.candidateName" :key="index" type="success">
+                {{ item }}
+              </el-tag>
+            </template>
+            <template v-else>
+              <el-tag type="success">
+                {{ scope.row.assigneeName || '鏃�' }}
+              </el-tag>
+            </template>
+          </template>
+        </el-table-column>
+        <el-table-column align="center" label="娴佺▼鐘舵��" min-width="70">
+          <template #default="scope">
+            <dict-tag :options="wf_business_status" :value="scope.row.businessStatus"></dict-tag>
+          </template>
+        </el-table-column>
+        <el-table-column align="center" prop="createTime" label="鍒涘缓鏃堕棿" width="160"></el-table-column>
+        <el-table-column label="鎿嶄綔" align="center" width="200">
+          <template #default="scope">
+            <el-button type="primary" size="small" icon="Edit" @click="handleOpen(scope.row)">鍔炵悊</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <pagination
+        v-show="total > 0"
+        v-model:page="queryParams.pageNum"
+        v-model:limit="queryParams.pageSize"
+        :total="total"
+        @pagination="handleQuery"
+      />
+    </el-card>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { getPageByTaskWait } from '@/api/workflow/task';
+import { TaskQuery, TaskVO } from '@/api/workflow/task/types';
+import workflowCommon from '@/api/workflow/workflowCommon';
+import { RouterJumpVo } from '@/api/workflow/workflowCommon/types';
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { wf_business_status } = toRefs<any>(proxy?.useDict('wf_business_status'));
+//鎻愪氦缁勪欢
+const queryFormRef = ref<ElFormInstance>();
+// 閬僵灞�
+const loading = ref(true);
+// 閫変腑鏁扮粍
+const ids = ref<Array<any>>([]);
+// 闈炲崟涓鐢�
+const single = ref(true);
+// 闈炲涓鐢�
+const multiple = ref(true);
+// 鏄剧ず鎼滅储鏉′欢
+const showSearch = ref(true);
+// 鎬绘潯鏁�
+const total = ref(0);
+// 妯″瀷瀹氫箟琛ㄦ牸鏁版嵁
+const taskList = ref([]);
+// 鏌ヨ鍙傛暟
+const queryParams = ref<TaskQuery>({
+  pageNum: 1,
+  pageSize: 10,
+  name: undefined,
+  processDefinitionName: undefined,
+  processDefinitionKey: undefined
+});
+onMounted(() => {
+  getWaitingList();
+});
+/** 鎼滅储鎸夐挳鎿嶄綔 */
+const handleQuery = () => {
+  getWaitingList();
+};
+/** 閲嶇疆鎸夐挳鎿嶄綔 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  queryParams.value.pageNum = 1;
+  queryParams.value.pageSize = 10;
+  handleQuery();
+};
+// 澶氶�夋閫変腑鏁版嵁
+const handleSelectionChange = (selection: any) => {
+  ids.value = selection.map((item: any) => item.id);
+  single.value = selection.length !== 1;
+  multiple.value = !selection.length;
+};
+//鍒嗛〉
+const getWaitingList = () => {
+  loading.value = true;
+  getPageByTaskWait(queryParams.value).then((resp) => {
+    taskList.value = resp.rows;
+    total.value = resp.total;
+    loading.value = false;
+  });
+};
+//鍔炵悊
+const handleOpen = async (row: TaskVO) => {
+  const routerJumpVo = reactive<RouterJumpVo>({
+    wfDefinitionConfigVo: row.wfDefinitionConfigVo,
+    wfNodeConfigVo: row.wfNodeConfigVo,
+    businessKey: row.businessKey,
+    taskId: row.id,
+    type: 'approval'
+  });
+  workflowCommon.routerJump(routerJumpVo, proxy);
+};
+</script>
diff --git a/tsconfig.json b/tsconfig.json
index 3dc32e7..e4bf3ac 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,26 +1,34 @@
 {
   "compilerOptions": {
-    "target": "es2022",
-    "useDefineForClassFields": true,
+    "target": "esnext",
     "module": "esnext",
-    "moduleResolution": "node",
+//    "useDefineForClassFields": true,
+    "moduleResolution": "bundler",
     "strict": true,
     "jsx": "preserve",
+    "strictNullChecks": false,
     "sourceMap": true,
     "resolveJsonModule": true,
     "esModuleInterop": true,
+    "strictFunctionTypes": false,
     "lib": ["esnext", "dom"],
+    "noImplicitAny": false,
     "baseUrl": ".",
     "allowJs": true,
+    "experimentalDecorators": true,
     "paths": {
       "@/*": ["src/*"]
     },
+    "compilerOptions": {
+      "types": ["element-plus/global"]
+    },
     "types": ["vite/client"],
     "skipLibCheck": true,
+    "removeComments": true,
     // 鍏佽榛樿瀵煎叆
     "allowSyntheticDefaultImports": true,
     "forceConsistentCasingInFileNames": true
   },
-  "include": ["src/**/*.ts", "src/**/*.vue", "src/types/**/*.d.ts"],
-  "exclude": ["node_modules", "dist", "**/*.js"]
+  "include": ["src/**/*.ts", "src/**/*.vue", "src/types/**/*.d.ts", "vite.config.ts"],
+  "exclude": ["node_modules", "dist", "**/*.js", "**/*.md", "src/**/*.md"]
 }
diff --git a/uno.config.ts b/uno.config.ts
new file mode 100644
index 0000000..0c60a22
--- /dev/null
+++ b/uno.config.ts
@@ -0,0 +1,33 @@
+import {
+  defineConfig,
+  presetAttributify,
+  presetIcons,
+  presetTypography,
+  presetUno,
+  presetWebFonts,
+  transformerDirectives,
+  transformerVariantGroup
+} from 'unocss';
+
+export default defineConfig({
+  shortcuts: {
+    'panel-title':
+      'pb-[5px] font-sans leading-[1.1] font-medium text-base text-[#6379bb] border-b border-b-solid border-[var(--el-border-color-light)] mb-5 mt-0'
+  },
+  theme: {
+    colors: {
+      primary: 'var(--el-color-primary)',
+      primary_dark: 'var(--el-color-primary-light-5)'
+    }
+  },
+  presets: [
+    presetUno(),
+    presetAttributify(),
+    presetIcons(),
+    presetTypography(),
+    presetWebFonts({
+      fonts: {}
+    })
+  ],
+  transformers: [transformerDirectives(), transformerVariantGroup()]
+});
diff --git a/vite.config.ts b/vite.config.ts
index 788aace..eea111e 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -13,8 +13,7 @@
     resolve: {
       alias: {
         '~': path.resolve(__dirname, './'),
-        '@': path.resolve(__dirname, './src'),
-        'vue-i18n': 'vue-i18n/dist/vue-i18n.cjs.js'
+        '@': path.resolve(__dirname, './src')
       },
       extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue']
     },
@@ -64,12 +63,31 @@
         '@vueuse/core',
         'path-to-regexp',
         'echarts',
-        '@wangeditor/editor',
-        '@wangeditor/editor-for-vue',
         'vue-i18n',
         '@vueup/vue-quill',
-        '@iconify/iconify',
+        'bpmn-js/lib/Viewer',
+        'bpmn-js/lib/Modeler.js',
+        'bpmn-js-properties-panel',
+        'min-dash',
+        'diagram-js/lib/navigation/movecanvas',
+        'diagram-js/lib/navigation/zoomscroll',
+        'bpmn-js/lib/features/palette/PaletteProvider',
+        'bpmn-js/lib/features/context-pad/ContextPadProvider',
+        'diagram-js/lib/draw/BaseRenderer',
+        'tiny-svg',
+        'image-conversion',
 
+        'element-plus/es/components/text/style/css',
+        'element-plus/es/components/collapse-item/style/css',
+        'element-plus/es/components/collapse/style/css',
+        'element-plus/es/components/space/style/css',
+        'element-plus/es/components/container/style/css',
+        'element-plus/es/components/aside/style/css',
+        'element-plus/es/components/main/style/css',
+        'element-plus/es/components/header/style/css',
+        'element-plus/es/components/button-group/style/css',
+        'element-plus/es/components/radio-button/style/css',
+        'element-plus/es/components/checkbox-group/style/css',
         'element-plus/es/components/form/style/css',
         'element-plus/es/components/form-item/style/css',
         'element-plus/es/components/button/style/css',
diff --git a/vite/plugins/compression.ts b/vite/plugins/compression.ts
index 123ff3a..aa8c779 100644
--- a/vite/plugins/compression.ts
+++ b/vite/plugins/compression.ts
@@ -1,6 +1,6 @@
 import compression from 'vite-plugin-compression';
 
-export default function createCompression(env: any) {
+export default (env: any) => {
   const { VITE_BUILD_COMPRESS } = env;
   const plugin: any[] = [];
   if (VITE_BUILD_COMPRESS) {
@@ -25,4 +25,4 @@
     }
   }
   return plugin;
-}
+};
diff --git a/vite/plugins/i18n.ts b/vite/plugins/i18n.ts
new file mode 100644
index 0000000..8777d1a
--- /dev/null
+++ b/vite/plugins/i18n.ts
@@ -0,0 +1,6 @@
+import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite';
+export default (path: any) => {
+  return VueI18nPlugin({
+    include: [path.resolve(__dirname, '../../src/lang/**.json')]
+  });
+};
diff --git a/vite/plugins/index.ts b/vite/plugins/index.ts
index a31412d..0ec5b8d 100644
--- a/vite/plugins/index.ts
+++ b/vite/plugins/index.ts
@@ -6,17 +6,19 @@
 import createSvgIconsPlugin from './svg-icon';
 import createCompression from './compression';
 import createSetupExtend from './setup-extend';
+import createI18n from './i18n';
 import path from 'path';
 
 export default (viteEnv: any, isBuild = false): [] => {
-  const vitePlusgins: any = [];
-  vitePlusgins.push(vue());
-  vitePlusgins.push(createUnoCss());
-  vitePlusgins.push(createAutoImport(path));
-  vitePlusgins.push(createComponents(path));
-  vitePlusgins.push(createCompression(viteEnv));
-  vitePlusgins.push(createIcons());
-  vitePlusgins.push(createSvgIconsPlugin(path, isBuild));
-  vitePlusgins.push(createSetupExtend());
-  return vitePlusgins;
+  const vitePlugins: any = [];
+  vitePlugins.push(vue());
+  vitePlugins.push(createUnoCss());
+  vitePlugins.push(createAutoImport(path));
+  vitePlugins.push(createComponents(path));
+  vitePlugins.push(createCompression(viteEnv));
+  vitePlugins.push(createIcons());
+  vitePlugins.push(createSvgIconsPlugin(path, isBuild));
+  vitePlugins.push(createSetupExtend());
+  vitePlugins.push(createI18n(path));
+  return vitePlugins;
 };
diff --git a/vite/plugins/setup-extend.ts b/vite/plugins/setup-extend.ts
index a1dbb5a..ed3423f 100644
--- a/vite/plugins/setup-extend.ts
+++ b/vite/plugins/setup-extend.ts
@@ -1,5 +1,5 @@
-import setupExtend from 'unplugin-vue-setup-extend-plus/vite'
+import setupExtend from 'unplugin-vue-setup-extend-plus/vite';
 
 export default () => {
-  return setupExtend({})
+  return setupExtend({});
 };
diff --git a/vite/plugins/unocss.ts b/vite/plugins/unocss.ts
index 76585c7..08e186b 100644
--- a/vite/plugins/unocss.ts
+++ b/vite/plugins/unocss.ts
@@ -1,14 +1,7 @@
 import UnoCss from 'unocss/vite';
-import { presetUno, presetAttributify, presetIcons } from 'unocss';
 
 export default () => {
   return UnoCss({
-    presets: [presetUno(), presetAttributify(), presetIcons()],
-    // rules: [['search', {}]],
-    shortcuts: {
-      'panel-title':
-        'pb-[5px] font-sans leading-[1.1] font-medium text-base text-[#6379bb] border-b border-b-solid border-[var(--el-border-color-light)] mb-5 mt-0'
-    },
     hmrTopLevelAwait: false // unocss榛樿鏄痶rue锛屼綆鐗堟湰娴忚鍣ㄦ槸涓嶆敮鎸佺殑锛屽惎鍔ㄥ悗浼氭姤閿�
   });
 };

--
Gitblit v1.9.3