瀏覽代碼

!88 【众测版】合并最新的 Vue3 重构
Merge pull request !88 from 芋道源码/dev

芋道源码 2 年之前
父節點
當前提交
d18ed70671
共有 100 個文件被更改,包括 2933 次插入2942 次删除
  1. 1 1
      .gitignore
  2. 6 5
      build/vite/index.ts
  3. 2 2
      index.html
  4. 33 30
      package.json
  5. 3 3
      src/api/bpm/definition/index.ts
  6. 6 6
      src/api/bpm/form/index.ts
  7. 7 7
      src/api/bpm/model/index.ts
  8. 1 0
      src/api/bpm/processInstance/index.ts
  9. 6 6
      src/api/bpm/userGroup/index.ts
  10. 2 22
      src/api/infra/apiAccessLog/index.ts
  11. 3 21
      src/api/infra/apiErrorLog/index.ts
  12. 78 12
      src/api/infra/codegen/index.ts
  13. 0 61
      src/api/infra/codegen/types.ts
  14. 1 8
      src/api/infra/config/index.ts
  15. 15 15
      src/api/infra/dataSourceConfig/index.ts
  16. 2 13
      src/api/infra/file/index.ts
  17. 8 13
      src/api/infra/fileConfig/index.ts
  18. 8 20
      src/api/infra/job/index.ts
  19. 3 19
      src/api/infra/jobLog/index.ts
  20. 60 0
      src/api/mall/product/category.ts
  21. 103 0
      src/api/mall/product/property.ts
  22. 6 1
      src/api/mp/account/index.ts
  23. 1 1
      src/api/mp/message/index.ts
  24. 14 7
      src/api/mp/tag/index.ts
  25. 9 8
      src/api/pay/merchant/index.ts
  26. 6 10
      src/api/system/area/index.ts
  27. 1 1
      src/api/system/dept/index.ts
  28. 21 8
      src/api/system/dict/dict.data.ts
  29. 16 8
      src/api/system/dict/dict.type.ts
  30. 0 46
      src/api/system/dict/types.ts
  31. 2 2
      src/api/system/errorCode/index.ts
  32. 3 9
      src/api/system/loginLog/index.ts
  33. 6 11
      src/api/system/mail/account/index.ts
  34. 2 12
      src/api/system/mail/log/index.ts
  35. 6 14
      src/api/system/mail/template/index.ts
  36. 5 10
      src/api/system/menu/index.ts
  37. 2 20
      src/api/system/notify/message/index.ts
  38. 9 13
      src/api/system/oauth2/client.ts
  39. 2 8
      src/api/system/oauth2/token.ts
  40. 2 10
      src/api/system/operatelog/index.ts
  41. 5 5
      src/api/system/permission/index.ts
  42. 7 19
      src/api/system/post/index.ts
  43. 17 14
      src/api/system/role/index.ts
  44. 15 21
      src/api/system/sensitiveWord/index.ts
  45. 6 13
      src/api/system/sms/smsChannel/index.ts
  46. 16 34
      src/api/system/sms/smsLog/index.ts
  47. 17 37
      src/api/system/sms/smsTemplate/index.ts
  48. 6 6
      src/api/system/tenant/index.ts
  49. 5 12
      src/api/system/tenantPackage/index.ts
  50. 10 25
      src/api/system/user/index.ts
  51. 二進制
      src/assets/imgs/profile.jpg
  52. 二進制
      src/assets/imgs/wechat.png
  53. 1 1
      src/components/ContentWrap/src/ContentWrap.vue
  54. 4 1
      src/components/Crontab/src/Crontab.vue
  55. 1 1
      src/components/Descriptions/src/Descriptions.vue
  56. 1 1
      src/components/DictTag/src/DictTag.vue
  57. 1 1
      src/components/Editor/src/Editor.vue
  58. 6 2
      src/components/Form/src/Form.vue
  59. 1 1
      src/components/Pagination/index.vue
  60. 10 2
      src/components/Search/src/Search.vue
  61. 4 2
      src/components/Table/src/Table.vue
  62. 0 50
      src/config/axios/request.ts
  63. 10 0
      src/hooks/web/useCrudSchemas.ts
  64. 3 1
      src/hooks/web/useTable.ts
  65. 2 2
      src/layout/components/Breadcrumb/src/Breadcrumb.vue
  66. 2 2
      src/layout/components/Message/src/Message.vue
  67. 2 1
      src/locales/en.ts
  68. 2 1
      src/locales/zh-CN.ts
  69. 49 8
      src/router/modules/remaining.ts
  70. 3 3
      src/store/modules/dict.ts
  71. 1 2
      src/types/auto-components.d.ts
  72. 2 0
      src/types/auto-imports.d.ts
  73. 2 2
      src/types/descriptions.d.ts
  74. 22 1
      src/utils/dict.ts
  75. 105 49
      src/utils/formatTime.ts
  76. 18 0
      src/utils/index.ts
  77. 5 5
      src/utils/tree.ts
  78. 2 2
      src/views/Profile/components/ProfileUser.vue
  79. 0 79
      src/views/bpm/definition/definition.data.ts
  80. 135 96
      src/views/bpm/definition/index.vue
  81. 106 0
      src/views/bpm/form/editor/index.vue
  82. 0 43
      src/views/bpm/form/form.data.ts
  83. 0 159
      src/views/bpm/form/formEditor.vue
  84. 144 66
      src/views/bpm/form/index.vue
  85. 130 0
      src/views/bpm/group/UserGroupForm.vue
  86. 0 64
      src/views/bpm/group/group.data.ts
  87. 158 156
      src/views/bpm/group/index.vue
  88. 227 0
      src/views/bpm/model/ModelForm.vue
  89. 137 0
      src/views/bpm/model/ModelImportForm.vue
  90. 102 0
      src/views/bpm/model/editor/index.vue
  91. 338 535
      src/views/bpm/model/index.vue
  92. 0 106
      src/views/bpm/model/model.data.ts
  93. 0 204
      src/views/bpm/model/modelEditor.vue
  94. 0 154
      src/views/bpm/processInstance/create.vue
  95. 130 0
      src/views/bpm/processInstance/create/index.vue
  96. 0 490
      src/views/bpm/processInstance/detail.vue
  97. 55 0
      src/views/bpm/processInstance/detail/ProcessInstanceBpmnViewer.vue
  98. 89 0
      src/views/bpm/processInstance/detail/ProcessInstanceTaskList.vue
  99. 81 0
      src/views/bpm/processInstance/detail/TaskUpdateAssigneeForm.vue
  100. 277 0
      src/views/bpm/processInstance/detail/index.vue

+ 1 - 1
.gitignore

@@ -8,4 +8,4 @@ dist-ssr
 pnpm-debug
 
 .idea
-.history
+.history

+ 6 - 5
build/vite/index.ts

@@ -15,7 +15,7 @@ import vueSetupExtend from 'vite-plugin-vue-setup-extend'
 import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite'
 import { createSvgIconsPlugin } from 'vite-plugin-svg-icons'
 
-export function createVitePlugins(VITE_APP_TITLE: string) {
+export function createVitePlugins() {
   const root = process.cwd()
   // 路径查找
   function pathResolve(dir: string) {
@@ -39,11 +39,14 @@ export function createVitePlugins(VITE_APP_TITLE: string) {
       imports: [
         'vue',
         'vue-router',
+        // 可额外添加需要 autoImport 的组件
         {
           '@/hooks/web/useI18n': ['useI18n'],
-          '@/hooks/web/useXTable': ['useXTable'],
           '@/hooks/web/useMessage': ['useMessage'],
+          '@/hooks/web/useXTable': ['useXTable'],
           '@/hooks/web/useVxeCrudSchemas': ['useVxeCrudSchemas'],
+          '@/hooks/web/useTable': ['useTable'],
+          '@/hooks/web/useCrudSchemas': ['useCrudSchemas'],
           '@/utils/formRules': ['required'],
           '@/utils/dict': ['DICT_TYPE']
         }
@@ -92,8 +95,6 @@ export function createVitePlugins(VITE_APP_TITLE: string) {
       ext: '.gz', // 生成的压缩包后缀
       deleteOriginFile: false //压缩后是否删除源文件
     }),
-    ViteEjsPlugin({
-      title: VITE_APP_TITLE
-    })
+    ViteEjsPlugin()
   ]
 }

+ 2 - 2
index.html

@@ -13,7 +13,7 @@
       name="description"
       content="芋道管理系统 基于 vue3 + CompositionAPI + typescript + vite3 + element plus 的后台开源免费管理系统!"
     />
-    <title><%= title %></title>
+    <title>%VITE_APP_TITLE%</title>
   </head>
   <body>
     <div id="app">
@@ -137,7 +137,7 @@
         <div class="app-loading-wrap">
           <div class="app-loading-title">
             <img src="/logo.gif" class="app-loading-logo" alt="Logo" />
-            <div class="app-loading-title"><%= title %></div>
+            <div class="app-loading-title">%VITE_APP_TITLE%</div>
           </div>
           <div class="app-loading-item">
             <div class="app-loading-outter"></div>

+ 33 - 30
package.json

@@ -1,6 +1,6 @@
 {
   "name": "yudao-ui-admin-vue3",
-  "version": "1.7.1-snapshot.1941",
+  "version": "1.7.1-snapshot.1961",
   "description": "基于vue3、vite4、element-plus、typesScript",
   "author": "xingyu",
   "private": false,
@@ -29,12 +29,14 @@
     "@form-create/designer": "^3.1.0",
     "@form-create/element-ui": "^3.1.17",
     "@iconify/iconify": "^3.1.0",
+    "@videojs-player/vue": "^1.0.0",
     "@vueuse/core": "^9.13.0",
     "@wangeditor/editor": "^5.1.23",
     "@wangeditor/editor-for-vue": "^5.1.10",
     "@zxcvbn-ts/core": "^2.2.1",
     "animate.css": "^4.1.1",
     "axios": "^1.3.4",
+    "benz-amr-recorder": "^1.1.5",
     "bpmn-js-token-simulation": "^0.10.0",
     "camunda-bpmn-moddle": "^7.0.1",
     "cropperjs": "^1.5.13",
@@ -43,7 +45,7 @@
     "diagram-js": "^11.6.0",
     "echarts": "^5.4.1",
     "echarts-wordcloud": "^2.1.0",
-    "element-plus": "2.2.34",
+    "element-plus": "2.3.1",
     "fast-xml-parser": "^4.1.3",
     "highlight.js": "^11.7.0",
     "intro.js": "^6.0.0",
@@ -57,62 +59,63 @@
     "qs": "^6.11.1",
     "steady-xml": "^0.1.0",
     "url": "^0.11.0",
+    "video.js": "^8.0.4",
     "vue": "3.2.47",
     "vue-i18n": "9.2.2",
     "vue-router": "^4.1.6",
     "vue-types": "^5.0.2",
     "vuedraggable": "^4.1.0",
-    "vxe-table": "^4.3.10",
+    "vxe-table": "^4.3.11",
     "web-storage-cache": "^1.1.1",
     "xe-utils": "^3.5.7",
     "xml-js": "^1.6.11"
   },
   "devDependencies": {
-    "@commitlint/cli": "^17.4.4",
+    "@commitlint/cli": "^17.5.0",
     "@commitlint/config-conventional": "^17.4.4",
-    "@iconify/json": "^2.2.31",
-    "@intlify/unplugin-vue-i18n": "^0.8.2",
+    "@iconify/json": "^2.2.38",
+    "@intlify/unplugin-vue-i18n": "^0.10.0",
     "@purge-icons/generated": "^0.9.0",
     "@types/intro.js": "^5.1.1",
-    "@types/lodash-es": "^4.17.6",
-    "@types/node": "^18.14.6",
+    "@types/lodash-es": "^4.17.7",
+    "@types/node": "^18.15.5",
     "@types/nprogress": "^0.2.0",
     "@types/qrcode": "^1.5.0",
     "@types/qs": "^6.9.7",
-    "@typescript-eslint/eslint-plugin": "^5.54.1",
-    "@typescript-eslint/parser": "^5.54.1",
-    "@vitejs/plugin-legacy": "^4.0.1",
-    "@vitejs/plugin-vue": "^4.0.0",
-    "@vitejs/plugin-vue-jsx": "^3.0.0",
-    "autoprefixer": "^10.4.13",
+    "@typescript-eslint/eslint-plugin": "^5.56.0",
+    "@typescript-eslint/parser": "^5.56.0",
+    "@vitejs/plugin-legacy": "^4.0.2",
+    "@vitejs/plugin-vue": "^4.1.0",
+    "@vitejs/plugin-vue-jsx": "^3.0.1",
+    "autoprefixer": "^10.4.14",
     "bpmn-js": "^8.9.0",
     "bpmn-js-properties-panel": "^0.46.0",
     "consola": "^2.15.3",
-    "eslint": "^8.35.0",
-    "eslint-config-prettier": "^8.7.0",
-    "eslint-define-config": "^1.15.0",
+    "eslint": "^8.36.0",
+    "eslint-config-prettier": "^8.8.0",
+    "eslint-define-config": "^1.17.0",
     "eslint-plugin-prettier": "^4.2.1",
     "eslint-plugin-vue": "^9.9.0",
-    "lint-staged": "^13.1.2",
+    "lint-staged": "^13.2.0",
     "postcss": "^8.4.21",
     "postcss-html": "^1.5.0",
     "postcss-scss": "^4.0.6",
-    "prettier": "^2.8.4",
-    "rimraf": "^4.3.1",
-    "rollup": "^3.18.0",
-    "sass": "^1.58.3",
-    "stylelint": "^15.2.0",
+    "prettier": "^2.8.6",
+    "rimraf": "^4.4.1",
+    "rollup": "^3.20.0",
+    "sass": "^1.59.3",
+    "stylelint": "^15.3.0",
     "stylelint-config-html": "^1.1.0",
     "stylelint-config-prettier": "^9.0.5",
-    "stylelint-config-recommended": "^10.0.1",
-    "stylelint-config-standard": "^30.0.1",
-    "stylelint-order": "^6.0.2",
-    "terser": "^5.16.5",
-    "typescript": "4.9.5",
+    "stylelint-config-recommended": "^11.0.0",
+    "stylelint-config-standard": "^31.0.0",
+    "stylelint-order": "^6.0.3",
+    "terser": "^5.16.6",
+    "typescript": "5.0.2",
     "unplugin-auto-import": "^0.15.1",
     "unplugin-element-plus": "^0.7.0",
     "unplugin-vue-components": "^0.24.1",
-    "vite": "4.1.4",
+    "vite": "4.2.1",
     "vite-plugin-compression": "^0.5.1",
     "vite-plugin-ejs": "^1.6.4",
     "vite-plugin-eslint": "^1.8.1",
@@ -125,7 +128,7 @@
     "windicss": "^3.5.6"
   },
   "engines": {
-    "node": ">=16.0.0"
+    "node": ">=16.18.0"
   },
   "license": "MIT",
   "repository": {

+ 3 - 3
src/api/bpm/definition/index.ts

@@ -1,19 +1,19 @@
 import request from '@/config/axios'
 
-export const getProcessDefinitionBpmnXMLApi = async (id: number) => {
+export const getProcessDefinitionBpmnXML = async (id: number) => {
   return await request.get({
     url: '/bpm/process-definition/get-bpmn-xml?id=' + id
   })
 }
 
-export const getProcessDefinitionPageApi = async (params) => {
+export const getProcessDefinitionPage = async (params) => {
   return await request.get({
     url: '/bpm/process-definition/page',
     params
   })
 }
 
-export const getProcessDefinitionListApi = async (params) => {
+export const getProcessDefinitionList = async (params) => {
   return await request.get({
     url: '/bpm/process-definition/list',
     params

+ 6 - 6
src/api/bpm/form/index.ts

@@ -11,7 +11,7 @@ export type FormVO = {
 }
 
 // 创建工作流的表单定义
-export const createFormApi = async (data: FormVO) => {
+export const createForm = async (data: FormVO) => {
   return await request.post({
     url: '/bpm/form/create',
     data: data
@@ -19,7 +19,7 @@ export const createFormApi = async (data: FormVO) => {
 }
 
 // 更新工作流的表单定义
-export const updateFormApi = async (data: FormVO) => {
+export const updateForm = async (data: FormVO) => {
   return await request.put({
     url: '/bpm/form/update',
     data: data
@@ -27,21 +27,21 @@ export const updateFormApi = async (data: FormVO) => {
 }
 
 // 删除工作流的表单定义
-export const deleteFormApi = async (id: number) => {
+export const deleteForm = async (id: number) => {
   return await request.delete({
     url: '/bpm/form/delete?id=' + id
   })
 }
 
 // 获得工作流的表单定义
-export const getFormApi = async (id: number) => {
+export const getForm = async (id: number) => {
   return await request.get({
     url: '/bpm/form/get?id=' + id
   })
 }
 
 // 获得工作流的表单定义分页
-export const getFormPageApi = async (params) => {
+export const getFormPage = async (params) => {
   return await request.get({
     url: '/bpm/form/page',
     params
@@ -49,7 +49,7 @@ export const getFormPageApi = async (params) => {
 }
 
 // 获得动态表单的精简列表
-export const getSimpleFormsApi = async () => {
+export const getSimpleFormList = async () => {
   return await request.get({
     url: '/bpm/form/list-all-simple'
   })

+ 7 - 7
src/api/bpm/model/index.ts

@@ -25,20 +25,20 @@ export type ModelVO = {
   bpmnXml: string
 }
 
-export const getModelPageApi = async (params) => {
+export const getModelPage = async (params) => {
   return await request.get({ url: '/bpm/model/page', params })
 }
 
-export const getModelApi = async (id: number) => {
+export const getModel = async (id: number) => {
   return await request.get({ url: '/bpm/model/get?id=' + id })
 }
 
-export const updateModelApi = async (data: ModelVO) => {
+export const updateModel = async (data: ModelVO) => {
   return await request.put({ url: '/bpm/model/update', data: data })
 }
 
 // 任务状态修改
-export const updateModelStateApi = async (id: number, state: number) => {
+export const updateModelState = async (id: number, state: number) => {
   const data = {
     id: id,
     state: state
@@ -46,14 +46,14 @@ export const updateModelStateApi = async (id: number, state: number) => {
   return await request.put({ url: '/bpm/model/update-state', data: data })
 }
 
-export const createModelApi = async (data: ModelVO) => {
+export const createModel = async (data: ModelVO) => {
   return await request.post({ url: '/bpm/model/create', data: data })
 }
 
-export const deleteModelApi = async (id: number) => {
+export const deleteModel = async (id: number) => {
   return await request.delete({ url: '/bpm/model/delete?id=' + id })
 }
 
-export const deployModelApi = async (id: number) => {
+export const deployModel = async (id: number) => {
   return await request.post({ url: '/bpm/model/deploy?id=' + id })
 }

+ 1 - 0
src/api/bpm/processInstance/index.ts

@@ -4,6 +4,7 @@ export type Task = {
   id: string
   name: string
 }
+
 export type ProcessInstanceVO = {
   id: number
   name: string

+ 6 - 6
src/api/bpm/userGroup/index.ts

@@ -11,7 +11,7 @@ export type UserGroupVO = {
 }
 
 // 创建用户组
-export const createUserGroupApi = async (data: UserGroupVO) => {
+export const createUserGroup = async (data: UserGroupVO) => {
   return await request.post({
     url: '/bpm/user-group/create',
     data: data
@@ -19,7 +19,7 @@ export const createUserGroupApi = async (data: UserGroupVO) => {
 }
 
 // 更新用户组
-export const updateUserGroupApi = async (data: UserGroupVO) => {
+export const updateUserGroup = async (data: UserGroupVO) => {
   return await request.put({
     url: '/bpm/user-group/update',
     data: data
@@ -27,21 +27,21 @@ export const updateUserGroupApi = async (data: UserGroupVO) => {
 }
 
 // 删除用户组
-export const deleteUserGroupApi = async (id: number) => {
+export const deleteUserGroup = async (id: number) => {
   return await request.delete({ url: '/bpm/user-group/delete?id=' + id })
 }
 
 // 获得用户组
-export const getUserGroupApi = async (id: number) => {
+export const getUserGroup = async (id: number) => {
   return await request.get({ url: '/bpm/user-group/get?id=' + id })
 }
 
 // 获得用户组分页
-export const getUserGroupPageApi = async (params) => {
+export const getUserGroupPage = async (params) => {
   return await request.get({ url: '/bpm/user-group/page', params })
 }
 
 // 获取用户组精简信息列表
-export const listSimpleUserGroupsApi = async () => {
+export const getSimpleUserGroupList = async (): Promise<UserGroupVO[]> => {
   return await request.get({ url: '/bpm/user-group/list-all-simple' })
 }

+ 2 - 22
src/api/infra/apiAccessLog/index.ts

@@ -19,32 +19,12 @@ export interface ApiAccessLogVO {
   createTime: Date
 }
 
-export interface ApiAccessLogPageReqVO extends PageParam {
-  userId?: number
-  userType?: number
-  applicationName?: string
-  requestUrl?: string
-  beginTime?: Date[]
-  duration?: number
-  resultCode?: number
-}
-
-export interface ApiAccessLogExportReqVO {
-  userId?: number
-  userType?: number
-  applicationName?: string
-  requestUrl?: string
-  beginTime?: Date[]
-  duration?: number
-  resultCode?: number
-}
-
 // 查询列表API 访问日志
-export const getApiAccessLogPageApi = (params: ApiAccessLogPageReqVO) => {
+export const getApiAccessLogPage = (params: PageParam) => {
   return request.get({ url: '/infra/api-access-log/page', params })
 }
 
 // 导出API 访问日志
-export const exportApiAccessLogApi = (params: ApiAccessLogExportReqVO) => {
+export const exportApiAccessLog = (params) => {
   return request.download({ url: '/infra/api-access-log/export-excel', params })
 }

+ 3 - 21
src/api/infra/apiErrorLog/index.ts

@@ -27,38 +27,20 @@ export interface ApiErrorLogVO {
   createTime: Date
 }
 
-export interface ApiErrorLogPageReqVO extends PageParam {
-  userId?: number
-  userType?: number
-  applicationName?: string
-  requestUrl?: string
-  exceptionTime?: Date[]
-  processStatus: number
-}
-
-export interface ApiErrorLogExportReqVO {
-  userId?: number
-  userType?: number
-  applicationName?: string
-  requestUrl?: string
-  exceptionTime?: Date[]
-  processStatus: number
-}
-
 // 查询列表API 访问日志
-export const getApiErrorLogPageApi = (params: ApiErrorLogPageReqVO) => {
+export const getApiErrorLogPage = (params: PageParam) => {
   return request.get({ url: '/infra/api-error-log/page', params })
 }
 
 // 更新 API 错误日志的处理状态
-export const updateApiErrorLogPageApi = (id: number, processStatus: number) => {
+export const updateApiErrorLogPage = (id: number, processStatus: number) => {
   return request.put({
     url: '/infra/api-error-log/update-status?id=' + id + '&processStatus=' + processStatus
   })
 }
 
 // 导出API 访问日志
-export const exportApiErrorLogApi = (params: ApiErrorLogExportReqVO) => {
+export const exportApiErrorLog = (params) => {
   return request.download({
     url: '/infra/api-error-log/export-excel',
     params

+ 78 - 12
src/api/infra/codegen/index.ts

@@ -1,57 +1,123 @@
 import request from '@/config/axios'
-import type { CodegenUpdateReqVO, CodegenCreateListReqVO } from './types'
+
+export type CodegenTableVO = {
+  id: number
+  tableId: number
+  isParentMenuIdValid: boolean
+  dataSourceConfigId: number
+  scene: number
+  tableName: string
+  tableComment: string
+  remark: string
+  moduleName: string
+  businessName: string
+  className: string
+  classComment: string
+  author: string
+  createTime: Date
+  updateTime: Date
+  templateType: number
+  parentMenuId: number
+}
+
+export type CodegenColumnVO = {
+  id: number
+  tableId: number
+  columnName: string
+  dataType: string
+  columnComment: string
+  nullable: number
+  primaryKey: number
+  autoIncrement: string
+  ordinalPosition: number
+  javaType: string
+  javaField: string
+  dictType: string
+  example: string
+  createOperation: number
+  updateOperation: number
+  listOperation: number
+  listOperationCondition: string
+  listOperationResult: number
+  htmlType: string
+}
+
+export type DatabaseTableVO = {
+  name: string
+  comment: string
+}
+
+export type CodegenDetailVO = {
+  table: CodegenTableVO
+  columns: CodegenColumnVO[]
+}
+
+export type CodegenPreviewVO = {
+  filePath: string
+  code: string
+}
+
+export type CodegenUpdateReqVO = {
+  table: CodegenTableVO | any
+  columns: CodegenColumnVO[]
+}
+
+export type CodegenCreateListReqVO = {
+  dataSourceConfigId: number
+  tableNames: string[]
+}
 
 // 查询列表代码生成表定义
-export const getCodegenTablePageApi = (params) => {
+export const getCodegenTablePage = (params: PageParam) => {
   return request.get({ url: '/infra/codegen/table/page', params })
 }
 
 // 查询详情代码生成表定义
-export const getCodegenTableApi = (id: number) => {
+export const getCodegenTable = (id: number) => {
   return request.get({ url: '/infra/codegen/detail?tableId=' + id })
 }
 
 // 新增代码生成表定义
-export const createCodegenTableApi = (data: CodegenCreateListReqVO) => {
+export const createCodegenTable = (data: CodegenCreateListReqVO) => {
   return request.post({ url: '/infra/codegen/create', data })
 }
 
 // 修改代码生成表定义
-export const updateCodegenTableApi = (data: CodegenUpdateReqVO) => {
+export const updateCodegenTable = (data: CodegenUpdateReqVO) => {
   return request.put({ url: '/infra/codegen/update', data })
 }
 
 // 基于数据库的表结构,同步数据库的表和字段定义
-export const syncCodegenFromDBApi = (id: number) => {
+export const syncCodegenFromDB = (id: number) => {
   return request.put({ url: '/infra/codegen/sync-from-db?tableId=' + id })
 }
 
 // 基于 SQL 建表语句,同步数据库的表和字段定义
-export const syncCodegenFromSQLApi = (id: number, sql: string) => {
+export const syncCodegenFromSQL = (id: number, sql: string) => {
   return request.put({ url: '/infra/codegen/sync-from-sql?tableId=' + id + '&sql=' + sql })
 }
 
 // 预览生成代码
-export const previewCodegenApi = (id: number) => {
+export const previewCodegen = (id: number) => {
   return request.get({ url: '/infra/codegen/preview?tableId=' + id })
 }
 
 // 下载生成代码
-export const downloadCodegenApi = (id: number) => {
+export const downloadCodegen = (id: number) => {
   return request.download({ url: '/infra/codegen/download?tableId=' + id })
 }
 
 // 获得表定义
-export const getSchemaTableListApi = (params) => {
+export const getSchemaTableList = (params) => {
   return request.get({ url: '/infra/codegen/db/table/list', params })
 }
 
 // 基于数据库的表结构,创建代码生成器的表定义
-export const createCodegenListApi = (data) => {
+export const createCodegenList = (data) => {
   return request.post({ url: '/infra/codegen/create-list', data })
 }
 
 // 删除代码生成表定义
-export const deleteCodegenTableApi = (id: number) => {
+export const deleteCodegenTable = (id: number) => {
   return request.delete({ url: '/infra/codegen/delete?tableId=' + id })
 }

+ 0 - 61
src/api/infra/codegen/types.ts

@@ -1,61 +0,0 @@
-export type CodegenTableVO = {
-  id: number
-  tableId: number
-  isParentMenuIdValid: boolean
-  dataSourceConfigId: number
-  scene: number
-  tableName: string
-  tableComment: string
-  remark: string
-  moduleName: string
-  businessName: string
-  className: string
-  classComment: string
-  author: string
-  createTime: Date
-  updateTime: Date
-  templateType: number
-  parentMenuId: number
-}
-
-export type CodegenColumnVO = {
-  id: number
-  tableId: number
-  columnName: string
-  dataType: string
-  columnComment: string
-  nullable: number
-  primaryKey: number
-  autoIncrement: string
-  ordinalPosition: number
-  javaType: string
-  javaField: string
-  dictType: string
-  example: string
-  createOperation: number
-  updateOperation: number
-  listOperation: number
-  listOperationCondition: string
-  listOperationResult: number
-  htmlType: string
-}
-export type DatabaseTableVO = {
-  name: string
-  comment: string
-}
-export type CodegenDetailVO = {
-  table: CodegenTableVO
-  columns: CodegenColumnVO[]
-}
-export type CodegenPreviewVO = {
-  filePath: string
-  code: string
-}
-export type CodegenUpdateReqVO = {
-  table: CodegenTableVO
-  columns: CodegenColumnVO[]
-}
-export type CodegenCreateListReqVO = {
-  dataSourceConfigId: number
-  tableNames: string[]
-}

+ 1 - 8
src/api/infra/config/index.ts

@@ -12,13 +12,6 @@ export interface ConfigVO {
   createTime: Date
 }
 
-export interface ConfigExportReqVO {
-  name?: string
-  key?: string
-  type?: number
-  createTime?: Date[]
-}
-
 // 查询参数列表
 export const getConfigPage = (params: PageParam) => {
   return request.get({ url: '/infra/config/page', params })
@@ -50,6 +43,6 @@ export const deleteConfig = (id: number) => {
 }
 
 // 导出参数
-export const exportConfigApi = (params: ConfigExportReqVO) => {
+export const exportConfigApi = (params) => {
   return request.download({ url: '/infra/config/export', params })
 }

+ 15 - 15
src/api/infra/dataSourceConfig/index.ts

@@ -1,35 +1,35 @@
 import request from '@/config/axios'
 
 export interface DataSourceConfigVO {
-  id: number
+  id: number | undefined
   name: string
   url: string
   username: string
   password: string
-  createTime: Date
-}
-
-// 查询数据源配置列表
-export const getDataSourceConfigListApi = () => {
-  return request.get({ url: '/infra/data-source-config/list' })
-}
-
-// 查询数据源配置详情
-export const getDataSourceConfigApi = (id: number) => {
-  return request.get({ url: '/infra/data-source-config/get?id=' + id })
+  createTime?: Date
 }
 
 // 新增数据源配置
-export const createDataSourceConfigApi = (data: DataSourceConfigVO) => {
+export const createDataSourceConfig = (data: DataSourceConfigVO) => {
   return request.post({ url: '/infra/data-source-config/create', data })
 }
 
 // 修改数据源配置
-export const updateDataSourceConfigApi = (data: DataSourceConfigVO) => {
+export const updateDataSourceConfig = (data: DataSourceConfigVO) => {
   return request.put({ url: '/infra/data-source-config/update', data })
 }
 
 // 删除数据源配置
-export const deleteDataSourceConfigApi = (id: number) => {
+export const deleteDataSourceConfig = (id: number) => {
   return request.delete({ url: '/infra/data-source-config/delete?id=' + id })
 }
+
+// 查询数据源配置详情
+export const getDataSourceConfig = (id: number) => {
+  return request.get({ url: '/infra/data-source-config/get?id=' + id })
+}
+
+// 查询数据源配置列表
+export const getDataSourceConfigList = () => {
+  return request.get({ url: '/infra/data-source-config/list' })
+}

+ 2 - 13
src/api/infra/fileList/index.ts → src/api/infra/file/index.ts

@@ -1,16 +1,5 @@
 import request from '@/config/axios'
 
-export interface FileVO {
-  id: number
-  configId: number
-  path: string
-  name: string
-  url: string
-  size: string
-  type: string
-  createTime: Date
-}
-
 export interface FilePageReqVO extends PageParam {
   path?: string
   type?: string
@@ -18,11 +7,11 @@ export interface FilePageReqVO extends PageParam {
 }
 
 // 查询文件列表
-export const getFilePageApi = (params: FilePageReqVO) => {
+export const getFilePage = (params: FilePageReqVO) => {
   return request.get({ url: '/infra/file/page', params })
 }
 
 // 删除文件
-export const deleteFileApi = (id: number) => {
+export const deleteFile = (id: number) => {
   return request.delete({ url: '/infra/file/delete?id=' + id })
 }

+ 8 - 13
src/api/infra/fileConfig/index.ts

@@ -13,6 +13,7 @@ export interface FileClientConfig {
   accessSecret?: string
   domain: string
 }
+
 export interface FileConfigVO {
   id: number
   name: string
@@ -24,43 +25,37 @@ export interface FileConfigVO {
   createTime: Date
 }
 
-export interface FileConfigPageReqVO extends PageParam {
-  name?: string
-  storage?: number
-  createTime?: Date[]
-}
-
 // 查询文件配置列表
-export const getFileConfigPageApi = (params: FileConfigPageReqVO) => {
+export const getFileConfigPage = (params: PageParam) => {
   return request.get({ url: '/infra/file-config/page', params })
 }
 
 // 查询文件配置详情
-export const getFileConfigApi = (id: number) => {
+export const getFileConfig = (id: number) => {
   return request.get({ url: '/infra/file-config/get?id=' + id })
 }
 
 // 更新文件配置为主配置
-export const updateFileConfigMasterApi = (id: number) => {
+export const updateFileConfigMaster = (id: number) => {
   return request.put({ url: '/infra/file-config/update-master?id=' + id })
 }
 
 // 新增文件配置
-export const createFileConfigApi = (data: FileConfigVO) => {
+export const createFileConfig = (data: FileConfigVO) => {
   return request.post({ url: '/infra/file-config/create', data })
 }
 
 // 修改文件配置
-export const updateFileConfigApi = (data: FileConfigVO) => {
+export const updateFileConfig = (data: FileConfigVO) => {
   return request.put({ url: '/infra/file-config/update', data })
 }
 
 // 删除文件配置
-export const deleteFileConfigApi = (id: number) => {
+export const deleteFileConfig = (id: number) => {
   return request.delete({ url: '/infra/file-config/delete?id=' + id })
 }
 
 // 测试文件配置
-export const testFileConfigApi = (id: number) => {
+export const testFileConfig = (id: number) => {
   return request.get({ url: '/infra/file-config/test?id=' + id })
 }

+ 8 - 20
src/api/infra/job/index.ts

@@ -13,50 +13,38 @@ export interface JobVO {
   createTime: Date
 }
 
-export interface JobPageReqVO extends PageParam {
-  name?: string
-  status?: number
-  handlerName?: string
-}
-
-export interface JobExportReqVO {
-  name?: string
-  status?: number
-  handlerName?: string
-}
-
 // 任务列表
-export const getJobPageApi = (params: JobPageReqVO) => {
+export const getJobPage = (params: PageParam) => {
   return request.get({ url: '/infra/job/page', params })
 }
 
 // 任务详情
-export const getJobApi = (id: number) => {
+export const getJob = (id: number) => {
   return request.get({ url: '/infra/job/get?id=' + id })
 }
 
 // 新增任务
-export const createJobApi = (data: JobVO) => {
+export const createJob = (data: JobVO) => {
   return request.post({ url: '/infra/job/create', data })
 }
 
 // 修改定时任务调度
-export const updateJobApi = (data: JobVO) => {
+export const updateJob = (data: JobVO) => {
   return request.put({ url: '/infra/job/update', data })
 }
 
 // 删除定时任务调度
-export const deleteJobApi = (id: number) => {
+export const deleteJob = (id: number) => {
   return request.delete({ url: '/infra/job/delete?id=' + id })
 }
 
 // 导出定时任务调度
-export const exportJobApi = (params: JobExportReqVO) => {
+export const exportJob = (params) => {
   return request.download({ url: '/infra/job/export-excel', params })
 }
 
 // 任务状态修改
-export const updateJobStatusApi = (id: number, status: number) => {
+export const updateJobStatus = (id: number, status: number) => {
   const params = {
     id,
     status
@@ -70,6 +58,6 @@ export const runJobApi = (id: number) => {
 }
 
 // 获得定时任务的下 n 次执行时间
-export const getJobNextTimesApi = (id: number) => {
+export const getJobNextTimes = (id: number) => {
   return request.get({ url: '/infra/job/get_next_times?id=' + id })
 }

+ 3 - 19
src/api/infra/jobLog/index.ts

@@ -14,34 +14,18 @@ export interface JobLogVO {
   createTime: string
 }
 
-export interface JobLogPageReqVO extends PageParam {
-  jobId?: number
-  handlerName?: string
-  beginTime?: string
-  endTime?: string
-  status?: number
-}
-
-export interface JobLogExportReqVO {
-  jobId?: number
-  handlerName?: string
-  beginTime?: string
-  endTime?: string
-  status?: number
-}
-
 // 任务日志列表
-export const getJobLogPageApi = (params: JobLogPageReqVO) => {
+export const getJobLogPage = (params: PageParam) => {
   return request.get({ url: '/infra/job-log/page', params })
 }
 
 // 任务日志详情
-export const getJobLogApi = (id: number) => {
+export const getJobLog = (id: number) => {
   return request.get({ url: '/infra/job-log/get?id=' + id })
 }
 
 // 导出定时任务日志
-export const exportJobLogApi = (params: JobLogExportReqVO) => {
+export const exportJobLog = (params) => {
   return request.download({
     url: '/infra/job-log/export-excel',
     params

+ 60 - 0
src/api/mall/product/category.ts

@@ -0,0 +1,60 @@
+import request from '@/config/axios'
+
+/**
+ * 产品分类
+ */
+export interface CategoryVO {
+  /**
+   * 分类编号
+   */
+  id?: number
+  /**
+   * 父分类编号
+   */
+  parentId?: number
+  /**
+   * 分类名称
+   */
+  name: string
+  /**
+   * 分类图片
+   */
+  picUrl: string
+  /**
+   * 分类排序
+   */
+  sort?: number
+  /**
+   * 分类描述
+   */
+  description?: string
+  /**
+   * 开启状态
+   */
+  status: number
+}
+
+// 创建商品分类
+export const createCategory = (data: CategoryVO) => {
+  return request.post({ url: '/product/category/create', data })
+}
+
+// 更新商品分类
+export const updateCategory = (data: CategoryVO) => {
+  return request.put({ url: '/product/category/update', data })
+}
+
+// 删除商品分类
+export const deleteCategory = (id: number) => {
+  return request.delete({ url: `/product/category/delete?id=${id}` })
+}
+
+// 获得商品分类
+export const getCategory = (id: number) => {
+  return request.get({ url: `/product/category/get?id=${id}` })
+}
+
+// 获得商品分类列表
+export const getCategoryList = (params: any) => {
+  return request.get({ url: '/product/category/list', params })
+}

+ 103 - 0
src/api/mall/product/property.ts

@@ -0,0 +1,103 @@
+import request from '@/config/axios'
+
+/**
+ * 商品属性
+ */
+export interface PropertyVO {
+  id?: number
+  /** 名称 */
+  name: string
+  /** 备注 */
+  remark?: string
+}
+
+/**
+ * 属性值
+ */
+export interface PropertyValueVO {
+  id?: number
+  /** 属性项的编号 */
+  propertyId?: number
+  /** 名称 */
+  name: string
+  /** 备注 */
+  remark?: string
+}
+
+/**
+ * 商品属性值的明细
+ */
+export interface PropertyValueDetailVO {
+  /** 属性项的编号 */
+  propertyId: number // 属性的编号
+  /** 属性的名称 */
+  propertyName: string
+  /** 属性值的编号 */
+  valueId: number
+  /** 属性值的名称 */
+  valueName: string
+}
+
+// ------------------------ 属性项 -------------------
+
+// 创建属性项
+export const createProperty = (data: PropertyVO) => {
+  return request.post({ url: '/product/property/create', data })
+}
+
+// 更新属性项
+export const updateProperty = (data: PropertyVO) => {
+  return request.put({ url: '/product/property/update', data })
+}
+
+// 删除属性项
+export const deleteProperty = (id: number) => {
+  return request.delete({ url: `/product/property/delete?id=${id}` })
+}
+
+// 获得属性项
+export const getProperty = (id: number): Promise<PropertyVO> => {
+  return request.get({ url: `/product/property/get?id=${id}` })
+}
+
+// 获得属性项分页
+export const getPropertyPage = (params: PageParam) => {
+  return request.get({ url: '/product/property/page', params })
+}
+
+// 获得属性项列表
+export const getPropertyList = (params: any) => {
+  return request.get({ url: '/product/property/list', params })
+}
+
+// 获得属性项列表
+export const getPropertyListAndValue = (params: any) => {
+  return request.get({ url: '/product/property/get-value-list', params })
+}
+
+// ------------------------ 属性值 -------------------
+
+// 获得属性值分页
+export const getPropertyValuePage = (params: PageParam & any) => {
+  return request.get({ url: '/product/property/value/page', params })
+}
+
+// 获得属性值
+export const getPropertyValue = (id: number): Promise<PropertyValueVO> => {
+  return request.get({ url: `/product/property/value/get?id=${id}` })
+}
+
+// 创建属性值
+export const createPropertyValue = (data: PropertyValueVO) => {
+  return request.post({ url: '/product/property/value/create', data })
+}
+
+// 更新属性值
+export const updatePropertyValue = (data: PropertyValueVO) => {
+  return request.put({ url: '/product/property/value/update', data })
+}
+
+// 删除属性值
+export const deletePropertyValue = (id: number) => {
+  return request.delete({ url: `/product/property/value/delete?id=${id}` })
+}

+ 6 - 1
src/api/mp/account/index.ts

@@ -1,5 +1,10 @@
 import request from '@/config/axios'
 
+export interface AccountVO {
+  id?: number
+  name: string
+}
+
 // 创建公众号账号
 export const createAccount = async (data) => {
   return await request.post({ url: '/mp/account/create', data })
@@ -26,7 +31,7 @@ export const getAccountPage = async (query) => {
 }
 
 // 获取公众号账号精简信息列表
-export const getSimpleAccounts = async () => {
+export const getSimpleAccountList = async () => {
   return request.get({ url: '/mp/account/list-all-simple' })
 }
 

+ 1 - 1
src/api/mp/message/index.ts

@@ -1,7 +1,7 @@
 import request from '@/config/axios'
 
 // 获得公众号消息分页
-export const getMessagePage = (query) => {
+export const getMessagePage = (query: PageParam) => {
   return request.get({
     url: '/mp/message/page',
     params: query

+ 14 - 7
src/api/mp/tag/index.ts

@@ -1,7 +1,14 @@
 import request from '@/config/axios'
 
+export interface TagVO {
+  id?: number
+  name: string
+  accountId: number
+  createTime: Date
+}
+
 // 创建公众号标签
-export const createTag = (data) => {
+export const createTag = (data: TagVO) => {
   return request.post({
     url: '/mp/tag/create',
     data: data
@@ -9,7 +16,7 @@ export const createTag = (data) => {
 }
 
 // 更新公众号标签
-export const updateTag = (data) => {
+export const updateTag = (data: TagVO) => {
   return request.put({
     url: '/mp/tag/update',
     data: data
@@ -17,21 +24,21 @@ export const updateTag = (data) => {
 }
 
 // 删除公众号标签
-export const deleteTag = (id) => {
+export const deleteTag = (id: number) => {
   return request.delete({
     url: '/mp/tag/delete?id=' + id
   })
 }
 
 // 获得公众号标签
-export const getTag = (id) => {
+export const getTag = (id: number) => {
   return request.get({
     url: '/mp/tag/get?id=' + id
   })
 }
 
 // 获得公众号标签分页
-export const getTagPage = (query) => {
+export const getTagPage = (query: PageParam) => {
   return request.get({
     url: '/mp/tag/page',
     params: query
@@ -39,14 +46,14 @@ export const getTagPage = (query) => {
 }
 
 // 获取公众号标签精简信息列表
-export const getSimpleTags = () => {
+export const getSimpleTagList = () => {
   return request.get({
     url: '/mp/tag/list-all-simple'
   })
 }
 
 // 同步公众号标签
-export const syncTag = (accountId) => {
+export const syncTag = (accountId: number) => {
   return request.post({
     url: '/mp/tag/sync?accountId=' + accountId
   })

+ 9 - 8
src/api/pay/merchant/index.ts

@@ -29,17 +29,17 @@ export interface MerchantExportReqVO {
 }
 
 // 查询列表支付商户
-export const getMerchantPageApi = (params: MerchantPageReqVO) => {
+export const getMerchantPage = (params: MerchantPageReqVO) => {
   return request.get({ url: '/pay/merchant/page', params })
 }
 
 // 查询详情支付商户
-export const getMerchantApi = (id: number) => {
+export const getMerchant = (id: number) => {
   return request.get({ url: '/pay/merchant/get?id=' + id })
 }
 
 // 根据商户名称搜索商户列表
-export const getMerchantListByNameApi = (name: string) => {
+export const getMerchantListByName = (name: string) => {
   return request.get({
     url: '/pay/merchant/list-by-name?id=',
     params: {
@@ -49,26 +49,27 @@ export const getMerchantListByNameApi = (name: string) => {
 }
 
 // 新增支付商户
-export const createMerchantApi = (data: MerchantVO) => {
+export const createMerchant = (data: MerchantVO) => {
   return request.post({ url: '/pay/merchant/create', data })
 }
 
 // 修改支付商户
-export const updateMerchantApi = (data: MerchantVO) => {
+export const updateMerchant = (data: MerchantVO) => {
   return request.put({ url: '/pay/merchant/update', data })
 }
 
 // 删除支付商户
-export const deleteMerchantApi = (id: number) => {
+export const deleteMerchant = (id: number) => {
   return request.delete({ url: '/pay/merchant/delete?id=' + id })
 }
 
 // 导出支付商户
-export const exportMerchantApi = (params: MerchantExportReqVO) => {
+export const exportMerchant = (params: MerchantExportReqVO) => {
   return request.download({ url: '/pay/merchant/export-excel', params })
 }
+
 // 支付商户状态修改
-export const changeMerchantStatusApi = (id: number, status: number) => {
+export const updateMerchantStatus = (id: number, status: number) => {
   const data = {
     id,
     status

+ 6 - 10
src/api/system/area/index.ts

@@ -1,15 +1,11 @@
-import request from '@/config/axios/request'
+import request from '@/config/axios'
 
 // 获得地区树
-export const getAreaTree = async (content?: any) => {
-  return await request.get({
-    url: '/system/area/tree',
-    params: content
-  })
+export const getAreaTree = async () => {
+  return await request.get({ url: '/system/area/tree' })
 }
+
 // 获得 IP 对应的地区名
-export const getAreaByIp = async (ip) => {
-  return await request.get({
-    url: '/system/area/get-by-ip?ip=' + ip
-  })
+export const getAreaByIp = async (ip: string) => {
+  return await request.get({ url: '/system/area/get-by-ip?ip=' + ip })
 }

+ 1 - 1
src/api/system/dept/index.ts

@@ -18,7 +18,7 @@ export interface DeptPageReqVO {
 }
 
 // 查询部门(精简)列表
-export const listSimpleDeptApi = async () => {
+export const getSimpleDeptList = async (): Promise<DeptVO[]> => {
   return await request.get({ url: '/system/dept/list-all-simple' })
 }
 

+ 21 - 8
src/api/system/dict/dict.data.ts

@@ -1,36 +1,49 @@
 import request from '@/config/axios'
-import type { DictDataVO, DictDataPageReqVO, DictDataExportReqVO } from './types'
+
+export type DictDataVO = {
+  id: number | undefined
+  sort: number | undefined
+  label: string
+  value: string
+  dictType: string
+  status: number
+  colorType: string
+  cssClass: string
+  remark: string
+  createTime: Date
+}
 
 // 查询字典数据(精简)列表
-export const listSimpleDictDataApi = () => {
+export const listSimpleDictData = () => {
   return request.get({ url: '/system/dict-data/list-all-simple' })
 }
 
 // 查询字典数据列表
-export const getDictDataPageApi = (params: DictDataPageReqVO) => {
+export const getDictDataPage = (params: PageParam) => {
   return request.get({ url: '/system/dict-data/page', params })
 }
 
 // 查询字典数据详情
-export const getDictDataApi = (id: number) => {
+export const getDictData = (id: number) => {
   return request.get({ url: '/system/dict-data/get?id=' + id })
 }
 
 // 新增字典数据
-export const createDictDataApi = (data: DictDataVO) => {
+export const createDictData = (data: DictDataVO) => {
   return request.post({ url: '/system/dict-data/create', data })
 }
 
 // 修改字典数据
-export const updateDictDataApi = (data: DictDataVO) => {
+export const updateDictData = (data: DictDataVO) => {
   return request.put({ url: '/system/dict-data/update', data })
 }
 
 // 删除字典数据
-export const deleteDictDataApi = (id: number) => {
+export const deleteDictData = (id: number) => {
   return request.delete({ url: '/system/dict-data/delete?id=' + id })
 }
+
 // 导出字典类型数据
-export const exportDictDataApi = (params: DictDataExportReqVO) => {
+export const exportDictDataApi = (params) => {
   return request.get({ url: '/system/dict-data/export', params })
 }

+ 16 - 8
src/api/system/dict/dict.type.ts

@@ -1,36 +1,44 @@
 import request from '@/config/axios'
-import type { DictTypeVO, DictTypePageReqVO, DictTypeExportReqVO } from './types'
+
+export type DictTypeVO = {
+  id: number | undefined
+  name: string
+  type: string
+  status: number
+  remark: string
+  createTime: Date
+}
 
 // 查询字典(精简)列表
-export const listSimpleDictTypeApi = () => {
+export const getSimpleDictTypeList = () => {
   return request.get({ url: '/system/dict-type/list-all-simple' })
 }
 
 // 查询字典列表
-export const getDictTypePageApi = (params: DictTypePageReqVO) => {
+export const getDictTypePage = (params: PageParam) => {
   return request.get({ url: '/system/dict-type/page', params })
 }
 
 // 查询字典详情
-export const getDictTypeApi = (id: number) => {
+export const getDictType = (id: number) => {
   return request.get({ url: '/system/dict-type/get?id=' + id })
 }
 
 // 新增字典
-export const createDictTypeApi = (data: DictTypeVO) => {
+export const createDictType = (data: DictTypeVO) => {
   return request.post({ url: '/system/dict-type/create', data })
 }
 
 // 修改字典
-export const updateDictTypeApi = (data: DictTypeVO) => {
+export const updateDictType = (data: DictTypeVO) => {
   return request.put({ url: '/system/dict-type/update', data })
 }
 
 // 删除字典
-export const deleteDictTypeApi = (id: number) => {
+export const deleteDictType = (id: number) => {
   return request.delete({ url: '/system/dict-type/delete?id=' + id })
 }
 // 导出字典类型
-export const exportDictTypeApi = (params: DictTypeExportReqVO) => {
+export const exportDictType = (params) => {
   return request.get({ url: '/system/dict-type/export', params })
 }

+ 0 - 46
src/api/system/dict/types.ts

@@ -1,46 +0,0 @@
-export type DictTypeVO = {
-  id: number
-  name: string
-  type: string
-  status: number
-  remark: string
-  createTime: Date
-}
-
-export type DictTypePageReqVO = {
-  name: string
-  type: string
-  status: number
-  createTime: Date[]
-}
-
-export type DictTypeExportReqVO = {
-  name: string
-  type: string
-  status: number
-  createTime: Date[]
-}
-
-export type DictDataVO = {
-  id: number
-  sort: number
-  label: string
-  value: string
-  dictType: string
-  status: number
-  colorType: string
-  cssClass: string
-  remark: string
-  createTime: Date
-}
-export type DictDataPageReqVO = {
-  label: string
-  dictType: string
-  status: number
-}
-
-export type DictDataExportReqVO = {
-  label: string
-  dictType: string
-  status: number
-}

+ 2 - 2
src/api/system/errorCode/index.ts

@@ -1,10 +1,10 @@
 import request from '@/config/axios'
 
 export interface ErrorCodeVO {
-  id: number
+  id: number | undefined
   type: number
   applicationName: string
-  code: number
+  code: number | undefined
   message: string
   memo: string
   createTime: Date

+ 3 - 9
src/api/system/loginLog/index.ts

@@ -13,18 +13,12 @@ export interface LoginLogVO {
   createTime: Date
 }
 
-export interface LoginLogReqVO extends PageParam {
-  userIp?: string
-  username?: string
-  status?: boolean
-  createTime?: Date[]
-}
-
 // 查询登录日志列表
-export const getLoginLogPageApi = (params: LoginLogReqVO) => {
+export const getLoginLogPage = (params: PageParam) => {
   return request.get({ url: '/system/login-log/page', params })
 }
+
 // 导出登录日志
-export const exportLoginLogApi = (params: LoginLogReqVO) => {
+export const exportLoginLog = (params) => {
   return request.download({ url: '/system/login-log/export', params })
 }

+ 6 - 11
src/api/system/mail/account/index.ts

@@ -10,37 +10,32 @@ export interface MailAccountVO {
   sslEnable: boolean
 }
 
-export interface MailAccountPageReqVO extends PageParam {
-  mail?: string
-  username?: string
-}
-
 // 查询邮箱账号列表
-export const getMailAccountPageApi = async (params: MailAccountPageReqVO) => {
+export const getMailAccountPage = async (params: PageParam) => {
   return await request.get({ url: '/system/mail-account/page', params })
 }
 
 // 查询邮箱账号详情
-export const getMailAccountApi = async (id: number) => {
+export const getMailAccount = async (id: number) => {
   return await request.get({ url: '/system/mail-account/get?id=' + id })
 }
 
 // 新增邮箱账号
-export const createMailAccountApi = async (data: MailAccountVO) => {
+export const createMailAccount = async (data: MailAccountVO) => {
   return await request.post({ url: '/system/mail-account/create', data })
 }
 
 // 修改邮箱账号
-export const updateMailAccountApi = async (data: MailAccountVO) => {
+export const updateMailAccount = async (data: MailAccountVO) => {
   return await request.put({ url: '/system/mail-account/update', data })
 }
 
 // 删除邮箱账号
-export const deleteMailAccountApi = async (id: number) => {
+export const deleteMailAccount = async (id: number) => {
   return await request.delete({ url: '/system/mail-account/delete?id=' + id })
 }
 
 // 获得邮箱账号精简列表
-export const getSimpleMailAccounts = async () => {
+export const getSimpleMailAccountList = async () => {
   return request.get({ url: '/system/mail-account/list-all-simple' })
 }

+ 2 - 12
src/api/system/mail/log/index.ts

@@ -19,22 +19,12 @@ export interface MailLogVO {
   sendException: string
 }
 
-export interface MailLogPageReqVO extends PageParam {
-  userId?: number
-  userType?: number
-  toMail?: string
-  accountId?: number
-  templateId?: number
-  sendStatus?: number
-  sendTime?: Date[]
-}
-
 // 查询邮件日志列表
-export const getMailLogPageApi = async (params: MailLogPageReqVO) => {
+export const getMailLogPage = async (params: PageParam) => {
   return await request.get({ url: '/system/mail-log/page', params })
 }
 
 // 查询邮件日志详情
-export const getMailLogApi = async (id: number) => {
+export const getMailLog = async (id: number) => {
   return await request.get({ url: '/system/mail-log/get?id=' + id })
 }

+ 6 - 14
src/api/system/mail/template/index.ts

@@ -13,14 +13,6 @@ export interface MailTemplateVO {
   remark: string
 }
 
-export interface MailTemplatePageReqVO extends PageParam {
-  name?: string
-  code?: string
-  accountId?: number
-  status?: number
-  createTime?: Date[]
-}
-
 export interface MailSendReqVO {
   mail: string
   templateCode: string
@@ -28,31 +20,31 @@ export interface MailSendReqVO {
 }
 
 // 查询邮件模版列表
-export const getMailTemplatePageApi = async (params: MailTemplatePageReqVO) => {
+export const getMailTemplatePage = async (params: PageParam) => {
   return await request.get({ url: '/system/mail-template/page', params })
 }
 
 // 查询邮件模版详情
-export const getMailTemplateApi = async (id: number) => {
+export const getMailTemplate = async (id: number) => {
   return await request.get({ url: '/system/mail-template/get?id=' + id })
 }
 
 // 新增邮件模版
-export const createMailTemplateApi = async (data: MailTemplateVO) => {
+export const createMailTemplate = async (data: MailTemplateVO) => {
   return await request.post({ url: '/system/mail-template/create', data })
 }
 
 // 修改邮件模版
-export const updateMailTemplateApi = async (data: MailTemplateVO) => {
+export const updateMailTemplate = async (data: MailTemplateVO) => {
   return await request.put({ url: '/system/mail-template/update', data })
 }
 
 // 删除邮件模版
-export const deleteMailTemplateApi = async (id: number) => {
+export const deleteMailTemplate = async (id: number) => {
   return await request.delete({ url: '/system/mail-template/delete?id=' + id })
 }
 
 // 发送邮件
-export const sendMailApi = (data: MailSendReqVO) => {
+export const sendMail = (data: MailSendReqVO) => {
   return request.post({ url: '/system/mail-template/send-mail', data })
 }

+ 5 - 10
src/api/system/menu/index.ts

@@ -18,18 +18,13 @@ export interface MenuVO {
   createTime: Date
 }
 
-export interface MenuPageReqVO {
-  name?: string
-  status?: number
-}
-
 // 查询菜单(精简)列表
-export const listSimpleMenusApi = () => {
+export const getSimpleMenusList = () => {
   return request.get({ url: '/system/menu/list-all-simple' })
 }
 
 // 查询菜单列表
-export const getMenuListApi = (params: MenuPageReqVO) => {
+export const getMenuList = (params) => {
   return request.get({ url: '/system/menu/list', params })
 }
 
@@ -39,16 +34,16 @@ export const getMenuApi = (id: number) => {
 }
 
 // 新增菜单
-export const createMenuApi = (data: MenuVO) => {
+export const createMenu = (data: MenuVO) => {
   return request.post({ url: '/system/menu/create', data })
 }
 
 // 修改菜单
-export const updateMenuApi = (data: MenuVO) => {
+export const updateMenu = (data: MenuVO) => {
   return request.put({ url: '/system/menu/update', data })
 }
 
 // 删除菜单
-export const deleteMenuApi = (id: number) => {
+export const deleteMenu = (id: number) => {
   return request.delete({ url: '/system/menu/delete?id=' + id })
 }

+ 2 - 20
src/api/system/notify/message/index.ts

@@ -15,31 +15,13 @@ export interface NotifyMessageVO {
   readTime: Date
 }
 
-export interface NotifyMessagePageReqVO extends PageParam {
-  userId?: number
-  userType?: number
-  templateCode?: string
-  templateType?: number
-  createTime?: Date[]
-}
-
-export interface NotifyMessageMyPageReqVO extends PageParam {
-  readStatus?: boolean
-  createTime?: Date[]
-}
-
 // 查询站内信消息列表
-export const getNotifyMessagePageApi = async (params: NotifyMessagePageReqVO) => {
+export const getNotifyMessagePage = async (params: PageParam) => {
   return await request.get({ url: '/system/notify-message/page', params })
 }
 
-// 查询站内信消息详情
-export const getNotifyMessageApi = async (id: number) => {
-  return await request.get({ url: '/system/notify-message/get?id=' + id })
-}
-
 // 获得我的站内信分页
-export const getMyNotifyMessagePage = async (params: NotifyMessageMyPageReqVO) => {
+export const getMyNotifyMessagePage = async (params: PageParam) => {
   return await request.get({ url: '/system/notify-message/my-page', params })
 }
 

+ 9 - 13
src/api/system/oauth2/client.ts

@@ -21,31 +21,27 @@ export interface OAuth2ClientVO {
   createTime: Date
 }
 
-export interface OAuth2ClientPageReqVO extends PageParam {
-  name?: string
-  status?: number
-}
-// 查询 OAuth2列表
-export const getOAuth2ClientPageApi = (params: OAuth2ClientPageReqVO) => {
+// 查询 OAuth2 客户端的列表
+export const getOAuth2ClientPage = (params: PageParam) => {
   return request.get({ url: '/system/oauth2-client/page', params })
 }
 
-// 查询 OAuth2详情
-export const getOAuth2ClientApi = (id: number) => {
+// 查询 OAuth2 客户端的详情
+export const getOAuth2Client = (id: number) => {
   return request.get({ url: '/system/oauth2-client/get?id=' + id })
 }
 
-// 新增 OAuth2
-export const createOAuth2ClientApi = (data: OAuth2ClientVO) => {
+// 新增 OAuth2 客户端
+export const createOAuth2Client = (data: OAuth2ClientVO) => {
   return request.post({ url: '/system/oauth2-client/create', data })
 }
 
-// 修改 OAuth2
-export const updateOAuth2ClientApi = (data: OAuth2ClientVO) => {
+// 修改 OAuth2 客户端
+export const updateOAuth2Client = (data: OAuth2ClientVO) => {
   return request.put({ url: '/system/oauth2-client/update', data })
 }
 
 // 删除 OAuth2
-export const deleteOAuth2ClientApi = (id: number) => {
+export const deleteOAuth2Client = (id: number) => {
   return request.delete({ url: '/system/oauth2-client/delete?id=' + id })
 }

+ 2 - 8
src/api/system/oauth2/token.ts

@@ -11,18 +11,12 @@ export interface OAuth2TokenVO {
   expiresTime: Date
 }
 
-export interface OAuth2TokenPageReqVO extends PageParam {
-  userId?: number
-  userType?: number
-  clientId?: string
-}
-
 // 查询 token列表
-export const getAccessTokenPageApi = (params: OAuth2TokenPageReqVO) => {
+export const getAccessTokenPage = (params: PageParam) => {
   return request.get({ url: '/system/oauth2-token/page', params })
 }
 
 // 删除 token
-export const deleteAccessTokenApi = (accessToken: number) => {
+export const deleteAccessToken = (accessToken: number) => {
   return request.delete({ url: '/system/oauth2-token/delete?accessToken=' + accessToken })
 }

+ 2 - 10
src/api/system/operatelog/index.ts

@@ -23,19 +23,11 @@ export type OperateLogVO = {
   resultData: string
 }
 
-export interface OperateLogPageReqVO extends PageParam {
-  module?: string
-  userNickname?: string
-  type?: number
-  success?: boolean
-  startTime?: Date[]
-}
-
 // 查询操作日志列表
-export const getOperateLogPageApi = (params: OperateLogPageReqVO) => {
+export const getOperateLogPage = (params: PageParam) => {
   return request.get({ url: '/system/operate-log/page', params })
 }
 // 导出操作日志
-export const exportOperateLogApi = (params: OperateLogPageReqVO) => {
+export const exportOperateLog = (params) => {
   return request.download({ url: '/system/operate-log/export', params })
 }

+ 5 - 5
src/api/system/permission/index.ts

@@ -17,26 +17,26 @@ export interface PermissionAssignRoleDataScopeReqVO {
 }
 
 // 查询角色拥有的菜单权限
-export const listRoleMenusApi = async (roleId: number) => {
+export const getRoleMenuList = async (roleId: number) => {
   return await request.get({ url: '/system/permission/list-role-resources?roleId=' + roleId })
 }
 
 // 赋予角色菜单权限
-export const assignRoleMenuApi = async (data: PermissionAssignRoleMenuReqVO) => {
+export const assignRoleMenu = async (data: PermissionAssignRoleMenuReqVO) => {
   return await request.post({ url: '/system/permission/assign-role-menu', data })
 }
 
 // 赋予角色数据权限
-export const assignRoleDataScopeApi = async (data: PermissionAssignRoleDataScopeReqVO) => {
+export const assignRoleDataScope = async (data: PermissionAssignRoleDataScopeReqVO) => {
   return await request.post({ url: '/system/permission/assign-role-data-scope', data })
 }
 
 // 查询用户拥有的角色数组
-export const listUserRolesApi = async (userId: number) => {
+export const getUserRoleList = async (userId: number) => {
   return await request.get({ url: '/system/permission/list-user-roles?userId=' + userId })
 }
 
 // 赋予用户角色
-export const aassignUserRoleApi = async (data: PermissionAssignUserRoleReqVO) => {
+export const assignUserRole = async (data: PermissionAssignUserRoleReqVO) => {
   return await request.post({ url: '/system/permission/assign-user-role', data })
 }

+ 7 - 19
src/api/system/post/index.ts

@@ -10,49 +10,37 @@ export interface PostVO {
   createTime?: Date
 }
 
-export interface PostPageReqVO extends PageParam {
-  code?: string
-  name?: string
-  status?: number
-}
-
-export interface PostExportReqVO {
-  code?: string
-  name?: string
-  status?: number
-}
-
 // 查询岗位列表
-export const getPostPageApi = async (params: PostPageReqVO) => {
+export const getPostPage = async (params: PageParam) => {
   return await request.get({ url: '/system/post/page', params })
 }
 
 // 获取岗位精简信息列表
-export const listSimplePostsApi = async () => {
+export const getSimplePostList = async (): Promise<PostVO[]> => {
   return await request.get({ url: '/system/post/list-all-simple' })
 }
 
 // 查询岗位详情
-export const getPostApi = async (id: number) => {
+export const getPost = async (id: number) => {
   return await request.get({ url: '/system/post/get?id=' + id })
 }
 
 // 新增岗位
-export const createPostApi = async (data: PostVO) => {
+export const createPost = async (data: PostVO) => {
   return await request.post({ url: '/system/post/create', data })
 }
 
 // 修改岗位
-export const updatePostApi = async (data: PostVO) => {
+export const updatePost = async (data: PostVO) => {
   return await request.put({ url: '/system/post/update', data })
 }
 
 // 删除岗位
-export const deletePostApi = async (id: number) => {
+export const deletePost = async (id: number) => {
   return await request.delete({ url: '/system/post/delete?id=' + id })
 }
 
 // 导出岗位
-export const exportPostApi = async (params: PostExportReqVO) => {
+export const exportPost = async (params) => {
   return await request.download({ url: '/system/post/export', params })
 }

+ 17 - 14
src/api/system/role/index.ts

@@ -7,52 +7,55 @@ export interface RoleVO {
   sort: number
   status: number
   type: number
+  dataScope: number
+  dataScopeDeptIds: number[]
   createTime: Date
 }
 
-export interface RolePageReqVO extends PageParam {
-  name?: string
-  code?: string
-  status?: number
-  createTime?: Date[]
-}
-
 export interface UpdateStatusReqVO {
   id: number
   status: number
 }
 
 // 查询角色列表
-export const getRolePageApi = async (params: RolePageReqVO) => {
+export const getRolePage = async (params: PageParam) => {
   return await request.get({ url: '/system/role/page', params })
 }
 
 // 查询角色(精简)列表
-export const listSimpleRolesApi = async () => {
+export const getSimpleRoleList = async (): Promise<RoleVO[]> => {
   return await request.get({ url: '/system/role/list-all-simple' })
 }
 
 // 查询角色详情
-export const getRoleApi = async (id: number) => {
+export const getRole = async (id: number) => {
   return await request.get({ url: '/system/role/get?id=' + id })
 }
 
 // 新增角色
-export const createRoleApi = async (data: RoleVO) => {
+export const createRole = async (data: RoleVO) => {
   return await request.post({ url: '/system/role/create', data })
 }
 
 // 修改角色
-export const updateRoleApi = async (data: RoleVO) => {
+export const updateRole = async (data: RoleVO) => {
   return await request.put({ url: '/system/role/update', data })
 }
 
 // 修改角色状态
-export const updateRoleStatusApi = async (data: UpdateStatusReqVO) => {
+export const updateRoleStatus = async (data: UpdateStatusReqVO) => {
   return await request.put({ url: '/system/role/update-status', data })
 }
 
 // 删除角色
-export const deleteRoleApi = async (id: number) => {
+export const deleteRole = async (id: number) => {
   return await request.delete({ url: '/system/role/delete?id=' + id })
 }
+
+// 导出角色
+export const exportRole = (params) => {
+  return request.download({
+    url: '/system/role/export-excel',
+    params
+  })
+}

+ 15 - 21
src/api/system/sensitiveWord/index.ts

@@ -1,4 +1,5 @@
 import request from '@/config/axios'
+import qs from 'qs'
 
 export interface SensitiveWordVO {
   id: number
@@ -9,56 +10,49 @@ export interface SensitiveWordVO {
   createTime: Date
 }
 
-export interface SensitiveWordPageReqVO extends PageParam {
-  name?: string
-  tag?: string
-  status?: number
-  createTime?: Date[]
-}
-
-export interface SensitiveWordExportReqVO {
-  name?: string
-  tag?: string
-  status?: number
-  createTime?: Date[]
+export interface SensitiveWordTestReqVO {
+  text: string
+  tag: string[]
 }
 
 // 查询敏感词列表
-export const getSensitiveWordPageApi = (params: SensitiveWordPageReqVO) => {
+export const getSensitiveWordPage = (params: PageParam) => {
   return request.get({ url: '/system/sensitive-word/page', params })
 }
 
 // 查询敏感词详情
-export const getSensitiveWordApi = (id: number) => {
+export const getSensitiveWord = (id: number) => {
   return request.get({ url: '/system/sensitive-word/get?id=' + id })
 }
 
 // 新增敏感词
-export const createSensitiveWordApi = (data: SensitiveWordVO) => {
+export const createSensitiveWord = (data: SensitiveWordVO) => {
   return request.post({ url: '/system/sensitive-word/create', data })
 }
 
 // 修改敏感词
-export const updateSensitiveWordApi = (data: SensitiveWordVO) => {
+export const updateSensitiveWord = (data: SensitiveWordVO) => {
   return request.put({ url: '/system/sensitive-word/update', data })
 }
 
 // 删除敏感词
-export const deleteSensitiveWordApi = (id: number) => {
+export const deleteSensitiveWord = (id: number) => {
   return request.delete({ url: '/system/sensitive-word/delete?id=' + id })
 }
 
 // 导出敏感词
-export const exportSensitiveWordApi = (params: SensitiveWordExportReqVO) => {
+export const exportSensitiveWord = (params) => {
   return request.download({ url: '/system/sensitive-word/export-excel', params })
 }
 
 // 获取所有敏感词的标签数组
-export const getSensitiveWordTagsApi = () => {
+export const getSensitiveWordTagList = () => {
   return request.get({ url: '/system/sensitive-word/get-tags' })
 }
 
 // 获得文本所包含的不合法的敏感词数组
-export const validateTextApi = (id: number) => {
-  return request.get({ url: '/system/sensitive-word/validate-text?' + id })
+export const validateText = (query: SensitiveWordTestReqVO) => {
+  return request.get({
+    url: '/system/sensitive-word/validate-text?' + qs.stringify(query, { arrayFormat: 'repeat' })
+  })
 }

+ 6 - 13
src/api/system/sms/smsChannel/index.ts

@@ -12,39 +12,32 @@ export interface SmsChannelVO {
   createTime: Date
 }
 
-export interface SmsChannelPageReqVO extends PageParam {
-  signature?: string
-  code?: string
-  status?: number
-  createTime?: Date[]
-}
-
 // 查询短信渠道列表
-export const getSmsChannelPageApi = (params: SmsChannelPageReqVO) => {
+export const getSmsChannelPage = (params: PageParam) => {
   return request.get({ url: '/system/sms-channel/page', params })
 }
 
 // 获得短信渠道精简列表
-export function getSimpleSmsChannels() {
+export function getSimpleSmsChannelList() {
   return request.get({ url: '/system/sms-channel/list-all-simple' })
 }
 
 // 查询短信渠道详情
-export const getSmsChannelApi = (id: number) => {
+export const getSmsChannel = (id: number) => {
   return request.get({ url: '/system/sms-channel/get?id=' + id })
 }
 
 // 新增短信渠道
-export const createSmsChannelApi = (data: SmsChannelVO) => {
+export const createSmsChannel = (data: SmsChannelVO) => {
   return request.post({ url: '/system/sms-channel/create', data })
 }
 
 // 修改短信渠道
-export const updateSmsChannelApi = (data: SmsChannelVO) => {
+export const updateSmsChannel = (data: SmsChannelVO) => {
   return request.put({ url: '/system/sms-channel/update', data })
 }
 
 // 删除短信渠道
-export const deleteSmsChannelApi = (id: number) => {
+export const deleteSmsChannel = (id: number) => {
   return request.delete({ url: '/system/sms-channel/delete?id=' + id })
 }

+ 16 - 34
src/api/system/sms/smsLog/index.ts

@@ -1,57 +1,39 @@
 import request from '@/config/axios'
 
 export interface SmsLogVO {
-  id: number
-  channelId: number
+  id: number | null
+  channelId: number | null
   channelCode: string
-  templateId: number
+  templateId: number | null
   templateCode: string
-  templateType: number
+  templateType: number | null
   templateContent: string
-  templateParams: Map<string, object>
+  templateParams: Map<string, object> | null
+  apiTemplateId: string
   mobile: string
-  userId: number
-  userType: number
-  sendStatus: number
-  sendTime: Date
-  sendCode: number
+  userId: number | null
+  userType: number | null
+  sendStatus: number | null
+  sendTime: Date | null
+  sendCode: number | null
   sendMsg: string
   apiSendCode: string
   apiSendMsg: string
   apiRequestId: string
   apiSerialNo: string
-  receiveStatus: number
-  receiveTime: Date
+  receiveStatus: number | null
+  receiveTime: Date | null
   apiReceiveCode: string
   apiReceiveMsg: string
-  createTime: Date
-}
-
-export interface SmsLogPageReqVO extends PageParam {
-  channelId?: number
-  templateId?: number
-  mobile?: string
-  sendStatus?: number
-  sendTime?: Date[]
-  receiveStatus?: number
-  receiveTime?: Date[]
-}
-export interface SmsLogExportReqVO {
-  channelId?: number
-  templateId?: number
-  mobile?: string
-  sendStatus?: number
-  sendTime?: Date[]
-  receiveStatus?: number
-  receiveTime?: Date[]
+  createTime: Date | null
 }
 
 // 查询短信日志列表
-export const getSmsLogPageApi = (params: SmsLogPageReqVO) => {
+export const getSmsLogPage = (params: PageParam) => {
   return request.get({ url: '/system/sms-log/page', params })
 }
 
 // 导出短信日志
-export const exportSmsLogApi = (params: SmsLogExportReqVO) => {
+export const exportSmsLog = (params) => {
   return request.download({ url: '/system/sms-log/export-excel', params })
 }

+ 17 - 37
src/api/system/sms/smsTemplate/index.ts

@@ -1,18 +1,18 @@
 import request from '@/config/axios'
 
 export interface SmsTemplateVO {
-  id: number
-  type: number
+  id: number | null
+  type: number | null
   status: number
   code: string
   name: string
   content: string
   remark: string
   apiTemplateId: string
-  channelId: number
-  channelCode: string
-  params: string[]
-  createTime: Date
+  channelId: number | null
+  channelCode?: string
+  params?: string[]
+  createTime?: Date
 }
 
 export interface SendSmsReqVO {
@@ -21,60 +21,40 @@ export interface SendSmsReqVO {
   templateParams: Map<String, Object>
 }
 
-export interface SmsTemplatePageReqVO {
-  type?: number
-  status?: number
-  code?: string
-  content?: string
-  apiTemplateId?: string
-  channelId?: number
-  createTime?: Date[]
-}
-
-export interface SmsTemplateExportReqVO {
-  type?: number
-  status?: number
-  code?: string
-  content?: string
-  apiTemplateId?: string
-  channelId?: number
-  createTime?: Date[]
-}
-
 // 查询短信模板列表
-export const getSmsTemplatePageApi = (params: SmsTemplatePageReqVO) => {
+export const getSmsTemplatePage = (params: PageParam) => {
   return request.get({ url: '/system/sms-template/page', params })
 }
 
 // 查询短信模板详情
-export const getSmsTemplateApi = (id: number) => {
+export const getSmsTemplate = (id: number) => {
   return request.get({ url: '/system/sms-template/get?id=' + id })
 }
 
 // 新增短信模板
-export const createSmsTemplateApi = (data: SmsTemplateVO) => {
+export const createSmsTemplate = (data: SmsTemplateVO) => {
   return request.post({ url: '/system/sms-template/create', data })
 }
 
 // 修改短信模板
-export const updateSmsTemplateApi = (data: SmsTemplateVO) => {
+export const updateSmsTemplate = (data: SmsTemplateVO) => {
   return request.put({ url: '/system/sms-template/update', data })
 }
 
 // 删除短信模板
-export const deleteSmsTemplateApi = (id: number) => {
+export const deleteSmsTemplate = (id: number) => {
   return request.delete({ url: '/system/sms-template/delete?id=' + id })
 }
 
-// 发送短信
-export const sendSmsApi = (data: SendSmsReqVO) => {
-  return request.post({ url: '/system/sms-template/send-sms', data })
-}
-
 // 导出短信模板
-export const exportPostApi = (params: SmsTemplateExportReqVO) => {
+export const exportSmsTemplate = (params) => {
   return request.download({
     url: '/system/sms-template/export-excel',
     params
   })
 }
+
+// 发送短信
+export const sendSms = (data: SendSmsReqVO) => {
+  return request.post({ url: '/system/sms-template/send-sms', data })
+}

+ 6 - 6
src/api/system/tenant/index.ts

@@ -32,31 +32,31 @@ export interface TenantExportReqVO {
 }
 
 // 查询租户列表
-export const getTenantPageApi = (params: TenantPageReqVO) => {
+export const getTenantPage = (params: TenantPageReqVO) => {
   return request.get({ url: '/system/tenant/page', params })
 }
 
 // 查询租户详情
-export const getTenantApi = (id: number) => {
+export const getTenant = (id: number) => {
   return request.get({ url: '/system/tenant/get?id=' + id })
 }
 
 // 新增租户
-export const createTenantApi = (data: TenantVO) => {
+export const createTenant = (data: TenantVO) => {
   return request.post({ url: '/system/tenant/create', data })
 }
 
 // 修改租户
-export const updateTenantApi = (data: TenantVO) => {
+export const updateTenant = (data: TenantVO) => {
   return request.put({ url: '/system/tenant/update', data })
 }
 
 // 删除租户
-export const deleteTenantApi = (id: number) => {
+export const deleteTenant = (id: number) => {
   return request.delete({ url: '/system/tenant/delete?id=' + id })
 }
 
 // 导出租户
-export const exportTenantApi = (params: TenantExportReqVO) => {
+export const exportTenant = (params: TenantExportReqVO) => {
   return request.download({ url: '/system/tenant/export-excel', params })
 }

+ 5 - 12
src/api/system/tenantPackage/index.ts

@@ -12,35 +12,28 @@ export interface TenantPackageVO {
   createTime: Date
 }
 
-export interface TenantPackagePageReqVO extends PageParam {
-  name?: string
-  status?: number
-  remark?: string
-  createTime?: Date[]
-}
-
 // 查询租户套餐列表
-export const getTenantPackageTypePageApi = (params: TenantPackagePageReqVO) => {
+export const getTenantPackagePage = (params: PageParam) => {
   return request.get({ url: '/system/tenant-package/page', params })
 }
 
 // 获得租户
-export const getTenantPackageApi = (id: number) => {
+export const getTenantPackage = (id: number) => {
   return request.get({ url: '/system/tenant-package/get?id=' + id })
 }
 
 // 新增租户套餐
-export const createTenantPackageTypeApi = (data: TenantPackageVO) => {
+export const createTenantPackage = (data: TenantPackageVO) => {
   return request.post({ url: '/system/tenant-package/create', data })
 }
 
 // 修改租户套餐
-export const updateTenantPackageTypeApi = (data: TenantPackageVO) => {
+export const updateTenantPackage = (data: TenantPackageVO) => {
   return request.put({ url: '/system/tenant-package/update', data })
 }
 
 // 删除租户套餐
-export const deleteTenantPackageTypeApi = (id: number) => {
+export const deleteTenantPackage = (id: number) => {
   return request.delete({ url: '/system/tenant-package/delete?id=' + id })
 }
 // 获取租户套餐精简信息列表

+ 10 - 25
src/api/system/user/index.ts

@@ -17,58 +17,43 @@ export interface UserVO {
   createTime: Date
 }
 
-export interface UserPageReqVO extends PageParam {
-  deptId?: number
-  username?: string
-  mobile?: string
-  status?: number
-  createTime?: Date[]
-}
-
-export interface UserExportReqVO {
-  code?: string
-  name?: string
-  status?: number
-  createTime?: Date[]
-}
-
 // 查询用户管理列表
-export const getUserPageApi = (params: UserPageReqVO) => {
+export const getUserPage = (params: PageParam) => {
   return request.get({ url: '/system/user/page', params })
 }
 
 // 查询用户详情
-export const getUserApi = (id: number) => {
+export const getUser = (id: number) => {
   return request.get({ url: '/system/user/get?id=' + id })
 }
 
 // 新增用户
-export const createUserApi = (data: UserVO) => {
+export const createUser = (data: UserVO) => {
   return request.post({ url: '/system/user/create', data })
 }
 
 // 修改用户
-export const updateUserApi = (data: UserVO) => {
+export const updateUser = (data: UserVO) => {
   return request.put({ url: '/system/user/update', data })
 }
 
 // 删除用户
-export const deleteUserApi = (id: number) => {
+export const deleteUser = (id: number) => {
   return request.delete({ url: '/system/user/delete?id=' + id })
 }
 
 // 导出用户
-export const exportUserApi = (params: UserExportReqVO) => {
+export const exportUser = (params) => {
   return request.download({ url: '/system/user/export', params })
 }
 
 // 下载用户导入模板
-export const importUserTemplateApi = () => {
+export const importUserTemplate = () => {
   return request.download({ url: '/system/user/get-import-template' })
 }
 
 // 用户密码重置
-export const resetUserPwdApi = (id: number, password: string) => {
+export const resetUserPwd = (id: number, password: string) => {
   const data = {
     id,
     password
@@ -77,7 +62,7 @@ export const resetUserPwdApi = (id: number, password: string) => {
 }
 
 // 用户状态修改
-export const updateUserStatusApi = (id: number, status: number) => {
+export const updateUserStatus = (id: number, status: number) => {
   const data = {
     id,
     status
@@ -86,6 +71,6 @@ export const updateUserStatusApi = (id: number, status: number) => {
 }
 
 // 获取用户精简信息列表
-export const getListSimpleUsersApi = () => {
+export const getSimpleUserList = (): Promise<UserVO[]> => {
   return request.get({ url: '/system/user/list-all-simple' })
 }

二進制
src/assets/imgs/profile.jpg


二進制
src/assets/imgs/wechat.png


+ 1 - 1
src/components/ContentWrap/src/ContentWrap.vue

@@ -13,7 +13,7 @@ defineProps({
 </script>
 
 <template>
-  <ElCard :class="[prefixCls, 'mb-20px']" shadow="never">
+  <ElCard :class="[prefixCls, 'mb-15px']" shadow="never">
     <template v-if="title" #header>
       <div class="flex items-center">
         <span class="text-16px font-700">{{ title }}</span>

+ 4 - 1
src/components/Crontab/src/Crontab.vue

@@ -6,7 +6,10 @@ interface shortcutsType {
   value: string
 }
 const props = defineProps({
-  modelValue: { type: String, default: '* * * * * ?' },
+  modelValue: {
+    type: String,
+    default: '* * * * * ?'
+  },
   shortcuts: { type: Array as PropType<shortcutsType[]>, default: () => [] }
 })
 const defaultValue = ref('')

+ 1 - 1
src/components/Descriptions/src/Descriptions.vue

@@ -76,7 +76,7 @@ const toggleClick = () => {
       v-if="title"
       :class="[
         `${prefixCls}-header`,
-        'h-50px flex justify-between items-center mb-10px border-bottom-1 border-solid border-[var(--tags-view-border-color)] px-10px cursor-pointer dark:border-[var(--el-border-color)]'
+        'h-50px flex justify-between items-center border-bottom-1 border-solid border-[var(--tags-view-border-color)] px-10px cursor-pointer dark:border-[var(--el-border-color)]'
       ]"
       @click="toggleClick"
     >

+ 1 - 1
src/components/DictTag/src/DictTag.vue

@@ -34,7 +34,7 @@ export default defineComponent({
         return null
       }
       // 解决自定义字典标签值为零时标签不渲染的问题
-      if (props.value === undefined) {
+      if (props.value === undefined || props.value === null) {
         return null
       }
       getDictObj(props.type, props.value.toString())

+ 1 - 1
src/components/Editor/src/Editor.vue

@@ -178,7 +178,7 @@ defineExpose({
 </script>
 
 <template>
-  <div class="border-1 border-solid border-[var(--tags-view-border-color)] z-3000">
+  <div class="border-1 border-solid border-[var(--tags-view-border-color)] z-99">
     <!-- 工具栏 -->
     <Toolbar
       :editor="editorRef"

+ 6 - 2
src/components/Form/src/Form.vue

@@ -35,7 +35,8 @@ export default defineComponent({
       default: () => []
     },
     // 是否需要栅格布局
-    isCol: propTypes.bool.def(true),
+    // update by 芋艿:将 true 改成 false,因为项目更常用这种方式
+    isCol: propTypes.bool.def(false),
     // 表单数据对象
     model: {
       type: Object as PropType<Recordable>,
@@ -46,7 +47,9 @@ export default defineComponent({
     // 是否自定义内容
     isCustom: propTypes.bool.def(false),
     // 表单label宽度
-    labelWidth: propTypes.oneOfType([String, Number]).def('auto')
+    labelWidth: propTypes.oneOfType([String, Number]).def('auto'),
+    // 是否 loading 数据中 add by 芋艿
+    vLoading: propTypes.bool.def(false)
   },
   emits: ['register'],
   setup(props, { slots, expose, emit }) {
@@ -280,6 +283,7 @@ export default defineComponent({
         {...getFormBindValue()}
         model={props.isCustom ? props.model : formModel}
         class={prefixCls}
+        v-loading={props.vLoading}
       >
         {{
           // 如果需要自定义,就什么都不渲染,而是提供默认插槽

+ 1 - 1
src/components/Pagination/index.vue

@@ -5,7 +5,7 @@
     class="float-right mt-15px mb-15px"
     :background="true"
     layout="total, sizes, prev, pager, next, jumper"
-    :page-sizes="[10, 20, 30, 50]"
+    :page-sizes="[10, 20, 30, 50, 100]"
     v-model:current-page="currentPage"
     v-model:page-size="pageSize"
     :pager-count="pagerCount"

+ 10 - 2
src/components/Search/src/Search.vue

@@ -98,6 +98,7 @@ const setVisible = () => {
 </script>
 
 <template>
+  <!-- update by 芋艿:class="-mb-15px" 用于降低和 ContentWrap 组件的底部距离,避免空隙过大 -->
   <Form
     :is-custom="false"
     :label-width="labelWidth"
@@ -106,21 +107,26 @@ const setVisible = () => {
     :is-col="isCol"
     :schema="newSchema"
     @register="register"
+    class="-mb-15px"
   >
     <template #action>
       <div v-if="layout === 'inline'">
-        <ElButton v-if="showSearch" type="primary" @click="search">
+        <!-- update by 芋艿:去除搜索的 type="primary",颜色变淡一点 -->
+        <ElButton v-if="showSearch" @click="search">
           <Icon icon="ep:search" class="mr-5px" />
           {{ t('common.query') }}
         </ElButton>
+        <!-- update by 芋艿:将 icon="ep:refresh-right" 修改成 icon="ep:refresh",和 ruoyi-vue 搜索保持一致  -->
         <ElButton v-if="showReset" @click="reset">
-          <Icon icon="ep:refresh-right" class="mr-5px" />
+          <Icon icon="ep:refresh" class="mr-5px" />
           {{ t('common.reset') }}
         </ElButton>
         <ElButton v-if="expand" text @click="setVisible">
           {{ t(visible ? 'common.shrink' : 'common.expand') }}
           <Icon :icon="visible ? 'ep:arrow-up' : 'ep:arrow-down'" />
         </ElButton>
+        <!-- add by 芋艿:补充在搜索后的按钮 -->
+        <slot name="actionMore"></slot>
       </div>
     </template>
     <template #[name] v-for="name in Object.keys($slots)" :key="name"
@@ -142,6 +148,8 @@ const setVisible = () => {
         {{ t(visible ? 'common.shrink' : 'common.expand') }}
         <Icon :icon="visible ? 'ep:arrow-up' : 'ep:arrow-down'" />
       </ElButton>
+      <!-- add by 芋艿:补充在搜索后的按钮 -->
+      <slot name="actionMore"></slot>
     </div>
   </template>
 </template>

+ 4 - 2
src/components/Table/src/Table.vue

@@ -104,11 +104,12 @@ export default defineComponent({
     })
 
     const pagination = computed(() => {
+      // update by 芋艿:保持和 Pagination 组件的逻辑一致
       return Object.assign(
         {
           small: false,
           background: true,
-          pagerCount: 5,
+          pagerCount: document.body.clientWidth < 992 ? 5 : 7,
           layout: 'total, sizes, prev, pager, next, jumper',
           pageSizes: [10, 20, 30, 50, 100],
           disabled: false,
@@ -283,10 +284,11 @@ export default defineComponent({
           }}
         </ElTable>
         {unref(getProps).pagination ? (
+          // update by 芋艿:保持和 Pagination 组件一致
           <ElPagination
             v-model:pageSize={pageSizeRef.value}
             v-model:currentPage={currentPageRef.value}
-            class="mt-10px"
+            class="float-right mt-15px mb-15px"
             {...unref(pagination)}
           ></ElPagination>
         ) : undefined}

+ 0 - 50
src/config/axios/request.ts

@@ -1,50 +0,0 @@
-import { service } from './service'
-
-import { config } from './config'
-
-const { default_headers } = config
-
-const request = (option: any) => {
-  const { url, method, params, data, headersType, responseType } = option
-  return service({
-    url: url,
-    method,
-    params,
-    data,
-    responseType: responseType,
-    headers: {
-      'Content-Type': headersType || default_headers
-    }
-  })
-}
-export default {
-  get: async <T = any>(option: any) => {
-    const res = await request({ method: 'GET', ...option })
-    return res as unknown as T
-  },
-  post: async <T = any>(option: any) => {
-    const res = await request({ method: 'POST', ...option })
-    return res as unknown as T
-  },
-  delete: async <T = any>(option: any) => {
-    const res = await request({ method: 'DELETE', ...option })
-    return res as unknown as T
-  },
-  put: async <T = any>(option: any) => {
-    const res = await request({ method: 'PUT', ...option })
-    return res as unknown as T
-  },
-  patch: async <T = any>(option: any) => {
-    const res = await request({ method: 'PATCH', ...option })
-    return res as unknown as T
-  },
-  download: async <T = any>(option: any) => {
-    const res = await request({ method: 'GET', responseType: 'blob', ...option })
-    return res as unknown as Promise<T>
-  },
-  upload: async <T = any>(option: any) => {
-    option.headersType = 'multipart/form-data'
-    const res = await request({ method: 'POST', ...option })
-    return res as unknown as Promise<T>
-  }
-}

+ 10 - 0
src/hooks/web/useCrudSchemas.ts

@@ -8,6 +8,7 @@ import { FormSchema } from '@/types/form'
 import { TableColumn } from '@/types/table'
 import { DescriptionsSchema } from '@/types/descriptions'
 import { ComponentOptions, ComponentProps } from '@/types/components'
+import { DictTag } from '@/components/DictTag'
 
 export type CrudSchema = Omit<TableColumn, 'children'> & {
   isSearch?: boolean // 是否在查询显示
@@ -151,6 +152,15 @@ const filterTableSchema = (crudSchema: CrudSchema[]): TableColumn[] => {
   const tableColumns = treeMap<CrudSchema>(crudSchema, {
     conversion: (schema: CrudSchema) => {
       if (schema?.isTable !== false && schema?.table?.show !== false) {
+        // add by 芋艿:增加对 dict 字典数据的支持
+        if (!schema.formatter && schema.dictType) {
+          schema.formatter = (_: Recordable, __: TableColumn, cellValue: any) => {
+            return h(DictTag, {
+              type: schema.dictType!, // ! 表示一定不为空
+              value: cellValue
+            })
+          }
+        }
         return {
           ...schema.table,
           ...schema

+ 3 - 1
src/hooks/web/useTable.ts

@@ -218,6 +218,8 @@ export const useTable = <T = any>(config?: UseTableConfig<T>) => {
     register,
     elTableRef,
     tableObject,
-    methods
+    methods,
+    // add by 芋艿:返回 tableMethods 属性,和 tableObject 更统一
+    tableMethods: methods
   }
 }

+ 2 - 2
src/layout/components/Breadcrumb/src/Breadcrumb.vue

@@ -37,7 +37,7 @@ export default defineComponent({
     })
 
     const getBreadcrumb = () => {
-      const currentPath = currentRoute.value.path
+      const currentPath = currentRoute.value.matched.slice(-1)[0].path
 
       levelList.value = filter<AppRouteRecordRaw>(unref(menuRouters), (node: AppRouteRecordRaw) => {
         return node.path === currentPath
@@ -47,7 +47,7 @@ export default defineComponent({
     const renderBreadcrumb = () => {
       const breadcrumbList = treeToList<AppRouteRecordRaw[]>(unref(levelList))
       return breadcrumbList.map((v) => {
-        const disabled = v.redirect === 'noredirect'
+        const disabled = !v.redirect || v.redirect === 'noredirect'
         const meta = v.meta as RouteMeta
         return (
           <ElBreadcrumbItem to={{ path: disabled ? '' : v.path }} key={v.name}>

+ 2 - 2
src/layout/components/Message/src/Message.vue

@@ -1,5 +1,5 @@
 <script setup lang="ts">
-import dayjs from 'dayjs'
+import { parseTime } from '@/utils/formatTime'
 import * as NotifyMessageApi from '@/api/system/notify/message'
 
 const { push } = useRouter()
@@ -57,7 +57,7 @@ onMounted(() => {
                     {{ item.templateNickname }}:{{ item.templateContent }}
                   </span>
                   <span class="message-date">
-                    {{ dayjs(item.createTime).format('YYYY-MM-DD HH:mm:ss') }}
+                    {{ parseTime(item.createTime) }}
                   </span>
                 </div>
               </div>

+ 2 - 1
src/locales/en.ts

@@ -297,7 +297,8 @@ export default {
     typeCreate: 'Dict Type Create',
     typeUpdate: 'Dict Type Eidt',
     dataCreate: 'Dict Data Create',
-    dataUpdate: 'Dict Data Eidt'
+    dataUpdate: 'Dict Data Eidt',
+    fileUpload: 'File Upload'
   },
   dialog: {
     dialog: 'Dialog',

+ 2 - 1
src/locales/zh-CN.ts

@@ -297,7 +297,8 @@ export default {
     typeCreate: '字典类型新增',
     typeUpdate: '字典类型编辑',
     dataCreate: '字典数据新增',
-    dataUpdate: '字典数据编辑'
+    dataUpdate: '字典数据编辑',
+    fileUpload: '上传文件'
   },
   dialog: {
     dialog: '弹窗',

+ 49 - 8
src/router/modules/remaining.ts

@@ -104,6 +104,31 @@ const remainingRouter: AppRouteRecordRaw[] = [
       }
     ]
   },
+
+  {
+    path: '/dict',
+    component: Layout,
+    name: 'dict',
+    meta: {
+      hidden: true
+    },
+    children: [
+      {
+        path: 'type/data/:dictType',
+        component: () => import('@/views/system/dict/data.vue'),
+        name: 'data',
+        meta: {
+          title: '字典数据',
+          noCache: true,
+          hidden: true,
+          canTo: true,
+          icon: '',
+          activeMenu: 'system/dict/data'
+        }
+      }
+    ]
+  },
+
   {
     path: '/codegen',
     component: Layout,
@@ -137,7 +162,7 @@ const remainingRouter: AppRouteRecordRaw[] = [
     children: [
       {
         path: 'job-log',
-        component: () => import('@/views/infra/job/JobLog.vue'),
+        component: () => import('@/views/infra/job/logger/index.vue'),
         name: 'JobLog',
         meta: {
           noCache: true,
@@ -200,26 +225,26 @@ const remainingRouter: AppRouteRecordRaw[] = [
     children: [
       {
         path: '/manager/form/edit',
-        component: () => import('@/views/bpm/form/formEditor.vue'),
+        component: () => import('@/views/bpm/form/editor/index.vue'),
         name: 'bpmFormEditor',
         meta: {
           noCache: true,
           hidden: true,
           canTo: true,
           title: '设计流程表单',
-          activeMenu: 'bpm/manager/form/formEditor'
+          activeMenu: '/bpm/manager/form'
         }
       },
       {
         path: '/manager/model/edit',
-        component: () => import('@/views/bpm/model/modelEditor.vue'),
+        component: () => import('@/views/bpm/model/editor/index.vue'),
         name: 'modelEditor',
         meta: {
           noCache: true,
           hidden: true,
           canTo: true,
           title: '设计流程',
-          activeMenu: 'bpm/manager/model/design'
+          activeMenu: '/bpm/manager/model'
         }
       },
       {
@@ -231,7 +256,7 @@ const remainingRouter: AppRouteRecordRaw[] = [
           hidden: true,
           canTo: true,
           title: '流程定义',
-          activeMenu: 'bpm/definition/index'
+          activeMenu: '/bpm/manager/model'
         }
       },
       {
@@ -247,7 +272,7 @@ const remainingRouter: AppRouteRecordRaw[] = [
       },
       {
         path: '/process-instance/create',
-        component: () => import('@/views/bpm/processInstance/create.vue'),
+        component: () => import('@/views/bpm/processInstance/create/index.vue'),
         name: 'BpmProcessInstanceCreate',
         meta: {
           noCache: true,
@@ -259,7 +284,7 @@ const remainingRouter: AppRouteRecordRaw[] = [
       },
       {
         path: '/process-instance/detail',
-        component: () => import('@/views/bpm/processInstance/detail.vue'),
+        component: () => import('@/views/bpm/processInstance/detail/index.vue'),
         name: 'BpmProcessInstanceDetail',
         meta: {
           noCache: true,
@@ -294,6 +319,22 @@ const remainingRouter: AppRouteRecordRaw[] = [
         }
       }
     ]
+  },
+  {
+    path: '/property',
+    component: Layout,
+    name: 'property',
+    meta: {
+      hidden: true
+    },
+    children: [
+      {
+        path: 'value/:propertyId(\\d+)',
+        component: () => import('@/views/mall/product/property/value/index.vue'),
+        name: 'PropertyValue',
+        meta: { title: '商品属性值', icon: '', activeMenu: '/product/property' }
+      }
+    ]
   }
 ]
 

+ 3 - 3
src/store/modules/dict.ts

@@ -3,7 +3,7 @@ import { store } from '../index'
 import { DictDataVO } from '@/api/system/dict/types'
 import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
 const { wsCache } = useCache('sessionStorage')
-import { listSimpleDictDataApi } from '@/api/system/dict/dict.data'
+import { listSimpleDictData } from '@/api/system/dict/dict.data'
 
 export interface DictValueType {
   value: any
@@ -44,7 +44,7 @@ export const useDictStore = defineStore('dict', {
         this.dictMap = dictMap
         this.isSetDict = true
       } else {
-        const res = await listSimpleDictDataApi()
+        const res = await listSimpleDictData()
         // 设置数据
         const dictDataMap = new Map<string, any>()
         res.forEach((dictData: DictDataVO) => {
@@ -74,7 +74,7 @@ export const useDictStore = defineStore('dict', {
     },
     async resetDict() {
       wsCache.delete(CACHE_KEY.DICT_CACHE)
-      const res = await listSimpleDictDataApi()
+      const res = await listSimpleDictData()
       // 设置数据
       const dictDataMap = new Map<string, any>()
       res.forEach((dictData: DictDataVO) => {

+ 1 - 2
src/types/auto-components.d.ts

@@ -24,7 +24,6 @@ declare module '@vue/runtime-core' {
     Echart: typeof import('./../components/Echart/src/Echart.vue')['default']
     Editor: typeof import('./../components/Editor/src/Editor.vue')['default']
     ElAutoResizer: typeof import('element-plus/es')['ElAutoResizer']
-    ElAvatar: typeof import('element-plus/es')['ElAvatar']
     ElBadge: typeof import('element-plus/es')['ElBadge']
     ElButton: typeof import('element-plus/es')['ElButton']
     ElButtonGroup: typeof import('element-plus/es')['ElButtonGroup']
@@ -69,10 +68,10 @@ declare module '@vue/runtime-core' {
     ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
     ElSelect: typeof import('element-plus/es')['ElSelect']
     ElSkeleton: typeof import('element-plus/es')['ElSkeleton']
-    ElSpace: typeof import('element-plus/es')['ElSpace']
     ElSwitch: typeof import('element-plus/es')['ElSwitch']
     ElTable: typeof import('element-plus/es')['ElTable']
     ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
+    ElTableV2: typeof import('element-plus/es')['ElTableV2']
     ElTabPane: typeof import('element-plus/es')['ElTabPane']
     ElTabs: typeof import('element-plus/es')['ElTabs']
     ElTag: typeof import('element-plus/es')['ElTag']

+ 2 - 0
src/types/auto-imports.d.ts

@@ -52,6 +52,7 @@ declare global {
   const triggerRef: typeof import('vue')['triggerRef']
   const unref: typeof import('vue')['unref']
   const useAttrs: typeof import('vue')['useAttrs']
+  const useCrudSchemas: typeof import('@/hooks/web/useCrudSchemas')['useCrudSchemas']
   const useCssModule: typeof import('vue')['useCssModule']
   const useCssVars: typeof import('vue')['useCssVars']
   const useI18n: typeof import('@/hooks/web/useI18n')['useI18n']
@@ -60,6 +61,7 @@ declare global {
   const useRoute: typeof import('vue-router')['useRoute']
   const useRouter: typeof import('vue-router')['useRouter']
   const useSlots: typeof import('vue')['useSlots']
+  const useTable: typeof import('@/hooks/web/useTable')['useTable']
   const useVxeCrudSchemas: typeof import('@/hooks/web/useVxeCrudSchemas')['useVxeCrudSchemas']
   const useXTable: typeof import('@/hooks/web/useXTable')['useXTable']
   const watch: typeof import('vue')['watch']

+ 2 - 2
src/types/descriptions.d.ts

@@ -8,6 +8,6 @@ export interface DescriptionsSchema {
   labelAlign?: 'left' | 'center' | 'right'
   className?: string
   labelClassName?: string
-  dateFormat?: string
-  dictType?: string
+  dateFormat?: string // add by 星语:支持时间的格式化
+  dictType?: string // add by 星语:支持 dict 字典数据
 }

+ 22 - 1
src/utils/dict.ts

@@ -70,6 +70,23 @@ export const getDictObj = (dictType: string, value: any) => {
   })
 }
 
+/**
+ * 获得字典数据的文本展示
+ *
+ * @param dictType 字典类型
+ * @param value 字典数据的值
+ */
+export const getDictLabel = (dictType: string, value: any) => {
+  const dictOptions: DictDataType[] = getDictOptions(dictType)
+  const dictLabel = ref('')
+  dictOptions.forEach((dict: DictDataType) => {
+    if (dict.value === value) {
+      dictLabel.value = dict.label
+    }
+  })
+  return dictLabel.value
+}
+
 export enum DICT_TYPE {
   USER_TYPE = 'user_type',
   COMMON_STATUS = 'common_status',
@@ -123,5 +140,9 @@ export enum DICT_TYPE {
   PAY_ORDER_STATUS = 'pay_order_status', // 商户支付订单状态
   PAY_ORDER_REFUND_STATUS = 'pay_order_refund_status', // 商户支付订单退款状态
   PAY_REFUND_ORDER_STATUS = 'pay_refund_order_status', // 退款订单状态
-  PAY_REFUND_ORDER_TYPE = 'pay_refund_order_type' // 退款订单类别
+  PAY_REFUND_ORDER_TYPE = 'pay_refund_order_type', // 退款订单类别
+
+  // ========== MP 模块 ==========
+  MP_AUTO_REPLY_REQUEST_MATCH = 'mp_auto_reply_request_match', // 自动回复请求匹配类型
+  MP_MESSAGE_TYPE = 'mp_message_type' // 消息类型
 }

+ 105 - 49
src/utils/formatTime.ts

@@ -11,58 +11,63 @@ import dayjs from 'dayjs'
  * @description format 季度 + 星期 + 几周:"YYYY-mm-dd HH:MM:SS WWW QQQQ ZZZ"
  * @returns 返回拼接后的时间字符串
  */
-export function formatDate(date: Date, format: string): string {
-  const we = date.getDay() // 星期
-  const z = getWeek(date) // 周
-  const qut = Math.floor((date.getMonth() + 3) / 3).toString() // 季度
-  const opt: { [key: string]: string } = {
-    'Y+': date.getFullYear().toString(), // 年
-    'm+': (date.getMonth() + 1).toString(), // 月(月份从0开始,要+1)
-    'd+': date.getDate().toString(), // 日
-    'H+': date.getHours().toString(), // 时
-    'M+': date.getMinutes().toString(), // 分
-    'S+': date.getSeconds().toString(), // 秒
-    'q+': qut // 季度
+export function formatDate(date: Date, format?: string): string {
+  // 日期不存在,则返回空
+  if (!date) {
+    return ''
   }
-  // 中文数字 (星期)
-  const week: { [key: string]: string } = {
-    '0': '日',
-    '1': '一',
-    '2': '二',
-    '3': '三',
-    '4': '四',
-    '5': '五',
-    '6': '六'
+  // 日期存在,则进行格式化
+  if (format === undefined) {
+    format = 'YYYY-MM-DD HH:mm:ss'
   }
-  // 中文数字(季度)
-  const quarter: { [key: string]: string } = {
-    '1': '一',
-    '2': '二',
-    '3': '三',
-    '4': '四'
+  return dayjs(date).format(format)
+}
+
+// TODO 芋艿:稍后去掉
+// 日期格式化
+export function parseTime(time: any, pattern?: string) {
+  if (arguments.length === 0 || !time) {
+    return null
+  }
+  const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}'
+  let date
+  if (typeof time === 'object') {
+    date = time
+  } else {
+    if (typeof time === 'string' && /^[0-9]+$/.test(time)) {
+      time = parseInt(time)
+    } else if (typeof time === 'string') {
+      time = time
+        .replace(new RegExp(/-/gm), '/')
+        .replace('T', ' ')
+        .replace(new RegExp(/\.\d{3}/gm), '')
+    }
+    if (typeof time === 'number' && time.toString().length === 10) {
+      time = time * 1000
+    }
+    date = new Date(time)
   }
-  if (/(W+)/.test(format))
-    format = format.replace(
-      RegExp.$1,
-      RegExp.$1.length > 1 ? (RegExp.$1.length > 2 ? '星期' + week[we] : '周' + week[we]) : week[we]
-    )
-  if (/(Q+)/.test(format))
-    format = format.replace(
-      RegExp.$1,
-      RegExp.$1.length == 4 ? '第' + quarter[qut] + '季度' : quarter[qut]
-    )
-  if (/(Z+)/.test(format))
-    format = format.replace(RegExp.$1, RegExp.$1.length == 3 ? '第' + z + '周' : z + '')
-  for (const k in opt) {
-    const r = new RegExp('(' + k + ')').exec(format)
-    // 若输入的长度不为1,则前面补零
-    if (r)
-      format = format.replace(
-        r[1],
-        RegExp.$1.length == 1 ? opt[k] : opt[k].padStart(RegExp.$1.length, '0')
-      )
+  const formatObj = {
+    y: date.getFullYear(),
+    m: date.getMonth() + 1,
+    d: date.getDate(),
+    h: date.getHours(),
+    i: date.getMinutes(),
+    s: date.getSeconds(),
+    a: date.getDay()
   }
-  return format
+  const time_str = format.replace(/{([ymdhisa])+}/g, (result, key) => {
+    let value = formatObj[key]
+    // Note: getDay() returns 0 on Sunday
+    if (key === 'a') {
+      return ['日', '一', '二', '三', '四', '五', '六'][value]
+    }
+    if (result.length > 0 && value < 10) {
+      value = '0' + value
+    }
+    return value || 0
+  })
+  return time_str
 }
 
 /**
@@ -189,5 +194,56 @@ export const dateFormatter = (row, column, cellValue) => {
   if (!cellValue) {
     return
   }
-  return dayjs(cellValue).format('YYYY-MM-DD HH:mm:ss')
+  return formatDate(cellValue)
+}
+
+/**
+ * 设置起始日期,时间为00:00:00
+ * @param param 传入日期
+ * @returns 带时间00:00:00的日期
+ */
+export function beginOfDay(param: Date) {
+  return new Date(param.getFullYear(), param.getMonth(), param.getDate(), 0, 0, 0, 0)
+}
+
+/**
+ * 设置结束日期,时间为23:59:59
+ * @param param 传入日期
+ * @returns 带时间23:59:59的日期
+ */
+export function endOfDay(param: Date) {
+  return new Date(param.getFullYear(), param.getMonth(), param.getDate(), 23, 59, 59, 999)
+}
+
+/**
+ * 计算两个日期间隔天数
+ * @param param1 日期1
+ * @param param2 日期2
+ */
+export function betweenDay(param1: Date, param2: Date) {
+  param1 = convertDate(param1)
+  param2 = convertDate(param2)
+  // 计算差值
+  return Math.floor((param2.getTime() - param1.getTime()) / (24 * 3600 * 1000))
+}
+
+/**
+ * 日期计算
+ * @param param1 日期
+ * @param param2 添加的时间
+ */
+export function addTime(param1: Date, param2: number) {
+  param1 = convertDate(param1)
+  return new Date(param1.getTime() + param2)
+}
+
+/**
+ * 日期转换
+ * @param param 日期
+ */
+export function convertDate(param: Date | string) {
+  if (typeof param === 'string') {
+    return new Date(param)
+  }
+  return param
 }

+ 18 - 0
src/utils/index.ts

@@ -137,3 +137,21 @@ export const generateUUID = () => {
     return (c === 'x' ? random : (random & 0x3) | 0x8).toString(16)
   })
 }
+
+/**
+ * element plus 的文件大小 Formatter 实现
+ *
+ * @param row 行数据
+ * @param column 字段
+ * @param cellValue 字段值
+ */
+// @ts-ignore
+export const fileSizeFormatter = (row, column, cellValue) => {
+  const fileSize = cellValue
+  const unitArr = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
+  const srcSize = parseFloat(fileSize)
+  const index = Math.floor(Math.log(srcSize) / Math.log(1024))
+  const size = srcSize / Math.pow(1024, index)
+  const sizeStr = size.toFixed(2) //保留的小数位数
+  return sizeStr + ' ' + unitArr[index]
+}

+ 5 - 5
src/utils/tree.ts

@@ -276,7 +276,7 @@ export const handleTree = (data: any[], id?: string, parentId?: string, children
 export const handleTree2 = (data, id, parentId, children, rootId) => {
   id = id || 'id'
   parentId = parentId || 'parentId'
-  children = children || 'children'
+  // children = children || 'children'
   rootId =
     rootId ||
     Math.min(
@@ -285,16 +285,16 @@ export const handleTree2 = (data, id, parentId, children, rootId) => {
       })
     ) ||
     0
-  //对源数据深度克隆
+  // 对源数据深度克隆
   const cloneData = JSON.parse(JSON.stringify(data))
-  //循环所有项
+  // 循环所有项
   const treeData = cloneData.filter((father) => {
     const branchArr = cloneData.filter((child) => {
-      //返回每一项的子级数组
+      // 返回每一项的子级数组
       return father[id] === child[parentId]
     })
     branchArr.length > 0 ? (father.children = branchArr) : ''
-    //返回第一层
+    // 返回第一层
     return father[parentId] === rootId
   })
   return treeData !== '' ? treeData : data

+ 2 - 2
src/views/Profile/components/ProfileUser.vue

@@ -34,13 +34,13 @@
       </li>
       <li class="list-group-item">
         <Icon icon="ep:calendar" class="mr-5px" />{{ t('profile.user.createTime') }}
-        <div class="pull-right">{{ dayjs(userInfo?.createTime).format('YYYY-MM-DD') }}</div>
+        <div class="pull-right">{{ parseTime(userInfo?.createTime) }}</div>
       </li>
     </ul>
   </div>
 </template>
 <script setup lang="ts">
-import dayjs from 'dayjs'
+import { parseTime } from '@/utils/formatTime'
 import UserAvatar from './UserAvatar.vue'
 
 import { getUserProfileApi, ProfileVO } from '@/api/system/user/profile'

+ 0 - 79
src/views/bpm/definition/definition.data.ts

@@ -1,79 +0,0 @@
-import type { VxeCrudSchema } from '@/hooks/web/useVxeCrudSchemas'
-
-// CrudSchema
-const crudSchemas = reactive<VxeCrudSchema>({
-  primaryKey: 'id',
-  primaryType: null,
-  action: true,
-  columns: [
-    {
-      title: '定义编号',
-      field: 'id',
-      table: {
-        width: 360
-      }
-    },
-    {
-      title: '定义名称',
-      field: 'name',
-      table: {
-        // width: 120,
-        slots: {
-          default: 'name_default'
-        }
-      }
-    },
-    {
-      title: '定义分类',
-      field: 'category',
-      // dictType: DICT_TYPE.BPM_MODEL_CATEGORY,
-      // dictClass: 'number',
-      table: {
-        // width: 120,
-        slots: {
-          default: 'category_default'
-        }
-      }
-    },
-    {
-      title: '表单信息',
-      field: 'formId',
-      table: {
-        // width: 200,
-        slots: {
-          default: 'formId_default'
-        }
-      }
-    },
-    {
-      title: '流程版本',
-      field: 'version',
-      table: {
-        // width: 80,
-        slots: {
-          default: 'version_default'
-        }
-      }
-    },
-    {
-      title: '激活状态',
-      field: 'suspensionState',
-      table: {
-        // width: 80,
-        slots: {
-          default: 'suspensionState_default'
-        }
-      }
-    },
-    {
-      title: '部署时间',
-      field: 'deploymentTime',
-      isForm: false,
-      formatter: 'formatDate'
-      // table: {
-      // width: 180
-      // }
-    }
-  ]
-})
-export const { allSchemas } = useVxeCrudSchemas(crudSchemas)

+ 135 - 96
src/views/bpm/definition/index.vue

@@ -1,92 +1,138 @@
 <template>
   <ContentWrap>
-    <!-- 列表 -->
-    <XTable @register="registerTable">
-      <!-- 流程名称 -->
-      <template #name_default="{ row }">
-        <XTextButton :title="row.name" @click="handleBpmnDetail(row.id)" />
-      </template>
-      <!-- 流程分类 -->
-      <template #category_default="{ row }">
-        <DictTag :type="DICT_TYPE.BPM_MODEL_CATEGORY" :value="Number(row?.category)" />
-      </template>
-      <!-- 表单信息 -->
-      <template #formId_default="{ row }">
-        <XTextButton
-          v-if="row.formType === 10"
-          :title="row.formName"
-          @click="handleFormDetail(row)"
-        />
-        <XTextButton v-else :title="row.formCustomCreatePath" @click="handleFormDetail(row)" />
-      </template>
-      <!-- 流程版本 -->
-      <template #version_default="{ row }">
-        <el-tag>v{{ row.version }}</el-tag>
-      </template>
-      <!-- 激活状态 -->
-      <template #suspensionState_default="{ row }">
-        <el-tag type="success" v-if="row.suspensionState === 1">激活</el-tag>
-        <el-tag type="warning" v-if="row.suspensionState === 2">挂起</el-tag>
-      </template>
-      <!-- 操作 -->
-      <template #actionbtns_default="{ row }">
-        <XTextButton
-          preIcon="ep:user"
-          title="分配规则"
-          v-hasPermi="['bpm:task-assign-rule:query']"
-          @click="handleAssignRule(row)"
-        />
-      </template>
-    </XTable>
-
-    <!-- 表单详情的弹窗 -->
-    <XModal v-model="formDetailVisible" width="800" title="表单详情" :show-footer="false">
-      <form-create
-        :rule="formDetailPreview.rule"
-        :option="formDetailPreview.option"
-        v-if="formDetailVisible"
+    <el-table v-loading="loading" :data="list">
+      <el-table-column label="定义编号" align="center" prop="id" width="400" />
+      <el-table-column label="流程名称" align="center" prop="name" width="200">
+        <template #default="scope">
+          <el-button type="text" @click="handleBpmnDetail(scope.row)">
+            <span>{{ scope.row.name }}</span>
+          </el-button>
+        </template>
+      </el-table-column>
+      <el-table-column label="定义分类" align="center" prop="category" width="100">
+        <template #default="scope">
+          <dict-tag :type="DICT_TYPE.BPM_MODEL_CATEGORY" :value="scope.row.category" />
+        </template>
+      </el-table-column>
+      <el-table-column label="表单信息" align="center" prop="formType" width="200">
+        <template #default="scope">
+          <el-button
+            v-if="scope.row.formType === 10"
+            type="text"
+            @click="handleFormDetail(scope.row)"
+          >
+            <span>{{ scope.row.formName }}</span>
+          </el-button>
+          <el-button v-else type="text" @click="handleFormDetail(scope.row)">
+            <span>{{ scope.row.formCustomCreatePath }}</span>
+          </el-button>
+        </template>
+      </el-table-column>
+      <el-table-column label="流程版本" align="center" prop="processDefinition.version" width="80">
+        <template #default="scope">
+          <el-tag v-if="scope.row">v{{ scope.row.version }}</el-tag>
+          <el-tag type="warning" v-else>未部署</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="状态" align="center" prop="version" width="80">
+        <template #default="scope">
+          <el-tag type="success" v-if="scope.row.suspensionState === 1">激活</el-tag>
+          <el-tag type="warning" v-if="scope.row.suspensionState === 2">挂起</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column
+        label="部署时间"
+        align="center"
+        prop="deploymentTime"
+        width="180"
+        :formatter="dateFormatter"
       />
-    </XModal>
-    <!-- 流程模型图的预览 -->
-    <XModal title="流程图" v-model="showBpmnOpen" width="80%" height="90%">
-      <my-process-viewer
-        key="designer"
-        v-model="bpmnXML"
-        :value="bpmnXML"
-        v-bind="bpmnControlForm"
-        :prefix="bpmnControlForm.prefix"
+      <el-table-column
+        label="定义描述"
+        align="center"
+        prop="description"
+        width="300"
+        show-overflow-tooltip
       />
-    </XModal>
+      <el-table-column label="操作" align="center" width="150" fixed="right">
+        <template #default="scope">
+          <el-button
+            link
+            type="primary"
+            @click="handleAssignRule(scope.row)"
+            v-hasPermi="['bpm:task-assign-rule:query']"
+          >
+            分配规则
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 分页 -->
+    <Pagination
+      :total="total"
+      v-model:page="queryParams.pageNo"
+      v-model:limit="queryParams.pageSize"
+      @pagination="getList"
+    />
   </ContentWrap>
+
+  <!-- 弹窗:表单详情 -->
+  <Dialog title="表单详情" v-model="formDetailVisible" width="800">
+    <form-create :rule="formDetailPreview.rule" :option="formDetailPreview.option" />
+  </Dialog>
+
+  <!-- 弹窗:流程模型图的预览 -->
+  <Dialog title="流程图" v-model="bpmnDetailVisible" width="800">
+    <my-process-viewer
+      key="designer"
+      v-model="bpmnXML"
+      :value="bpmnXML"
+      v-bind="bpmnControlForm"
+      :prefix="bpmnControlForm.prefix"
+    />
+  </Dialog>
 </template>
-<script setup lang="ts">
-// 业务相关的 import
+
+<script setup lang="ts" name="Form">
+import { DICT_TYPE } from '@/utils/dict'
+import { dateFormatter } from '@/utils/formatTime'
 import * as DefinitionApi from '@/api/bpm/definition'
-// import * as ModelApi from '@/api/bpm/model'
-import { allSchemas } from './definition.data'
 import { setConfAndFields2 } from '@/utils/formCreate'
-import { DICT_TYPE } from '@/utils/dict'
-
-const bpmnXML = ref(null)
-const showBpmnOpen = ref(false)
-const bpmnControlForm = ref({
-  prefix: 'flowable'
-})
-// const message = useMessage() // 消息弹窗
-const router = useRouter() // 路由
+const { push } = useRouter() // 路由
 const { query } = useRoute() // 查询参数
 
-// ========== 列表相关 ==========
+const loading = ref(true) // 列表的加载中
+const total = ref(0) // 列表的总页数
+const list = ref([]) // 列表的数据
 const queryParams = reactive({
+  pageNo: 1,
+  pageSize: 10,
   key: query.key
 })
-const [registerTable] = useXTable({
-  allSchemas: allSchemas,
-  getListApi: DefinitionApi.getProcessDefinitionPageApi,
-  params: queryParams
-})
 
-// 流程表单的详情按钮操作
+/** 查询列表 */
+const getList = async () => {
+  loading.value = true
+  try {
+    const data = await DefinitionApi.getProcessDefinitionPage(queryParams)
+    list.value = data.list
+    total.value = data.total
+  } finally {
+    loading.value = false
+  }
+}
+
+/** 点击任务分配按钮 */
+const handleAssignRule = (row) => {
+  push({
+    name: 'BpmTaskAssignRuleList',
+    query: {
+      modelId: row.id
+    }
+  })
+}
+
+/** 流程表单的详情按钮操作 */
 const formDetailVisible = ref(false)
 const formDetailPreview = ref({
   rule: [],
@@ -99,32 +145,25 @@ const handleFormDetail = async (row) => {
     // 弹窗打开
     formDetailVisible.value = true
   } else {
-    await router.push({
+    await push({
       path: row.formCustomCreatePath
     })
   }
 }
 
-// 流程图的详情按钮操作
-const handleBpmnDetail = (row) => {
-  // TODO 芋艿:流程组件开发中
-  console.log(row)
-  DefinitionApi.getProcessDefinitionBpmnXMLApi(row).then((response) => {
-    console.log(response, 'response')
-    bpmnXML.value = response
-    // 弹窗打开
-    showBpmnOpen.value = true
-  })
-  // message.success('流程组件开发中,预计 2 月底完成')
+/** 流程图的详情按钮操作 */
+const bpmnDetailVisible = ref(false)
+const bpmnXML = ref(null)
+const bpmnControlForm = ref({
+  prefix: 'flowable'
+})
+const handleBpmnDetail = async (row) => {
+  bpmnXML.value = await DefinitionApi.getProcessDefinitionBpmnXML(row.id)
+  bpmnDetailVisible.value = true
 }
 
-// 点击任务分配按钮
-const handleAssignRule = (row) => {
-  router.push({
-    name: 'BpmTaskAssignRuleList',
-    query: {
-      processDefinitionId: row.id
-    }
-  })
-}
+/** 初始化 **/
+onMounted(() => {
+  getList()
+})
 </script>

+ 106 - 0
src/views/bpm/form/editor/index.vue

@@ -0,0 +1,106 @@
+<template>
+  <ContentWrap>
+    <!-- 表单设计器 -->
+    <fc-designer ref="designer" height="780px">
+      <template #handle>
+        <el-button round size="small" type="primary" @click="handleSave">
+          <Icon icon="ep:plus" class="mr-5px" /> 保存
+        </el-button>
+      </template>
+    </fc-designer>
+  </ContentWrap>
+
+  <!-- 表单保存的弹窗 -->
+  <Dialog title="保存表单" v-model="modelVisible" width="600">
+    <el-form ref="formRef" :model="formData" :rules="formRules" label-width="80px">
+      <el-form-item label="表单名" prop="name">
+        <el-input v-model="formData.name" placeholder="请输入表单名" />
+      </el-form-item>
+      <el-form-item label="状态" prop="status">
+        <el-radio-group v-model="formData.status">
+          <el-radio
+            v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
+            :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="formData.remark" type="textarea" placeholder="请输入备注" />
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
+      <el-button @click="modelVisible = false">取 消</el-button>
+    </template>
+  </Dialog>
+</template>
+<script setup lang="ts">
+import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
+import { CommonStatusEnum } from '@/utils/constants'
+import * as FormApi from '@/api/bpm/form'
+import { encodeConf, encodeFields, setConfAndFields } from '@/utils/formCreate'
+
+const { t } = useI18n() // 国际化
+const message = useMessage() // 消息
+const { query } = useRoute() // 路由
+
+const designer = ref() // 表单设计器
+const modelVisible = ref(false) // 弹窗是否展示
+const formLoading = ref(false) // 表单的加载中:提交的按钮禁用
+const formData = ref({
+  name: '',
+  status: CommonStatusEnum.ENABLE,
+  remark: ''
+})
+const formRules = reactive({
+  name: [{ required: true, message: '表单名不能为空', trigger: 'blur' }],
+  status: [{ required: true, message: '开启状态不能为空', trigger: 'blur' }]
+})
+const formRef = ref() // 表单 Ref
+
+/** 处理保存按钮 */
+const handleSave = () => {
+  modelVisible.value = true
+}
+
+/** 提交表单 */
+const submitForm = async () => {
+  // 校验表单
+  if (!formRef) return
+  const valid = await formRef.value.validate()
+  if (!valid) return
+  // 提交请求
+  formLoading.value = true
+  try {
+    const data = formData.value as FormApi.FormVO
+    data.conf = encodeConf(designer) // 表单配置
+    data.fields = encodeFields(designer) // 表单字段
+    if (!data.id) {
+      await FormApi.createForm(data)
+      message.success(t('common.createSuccess'))
+    } else {
+      await FormApi.updateForm(data)
+      message.success(t('common.updateSuccess'))
+    }
+    modelVisible.value = false
+  } finally {
+    formLoading.value = false
+  }
+}
+
+/** 初始化 **/
+onMounted(async () => {
+  // 场景一:新增表单
+  const id = query.id as unknown as number
+  if (!id) {
+    return
+  }
+  // 场景二:修改表单
+  const data = await FormApi.getForm(id)
+  formData.value = data
+  setConfAndFields(designer, data.conf, data.fields)
+})
+</script>

+ 0 - 43
src/views/bpm/form/form.data.ts

@@ -1,43 +0,0 @@
-import type { VxeCrudSchema } from '@/hooks/web/useVxeCrudSchemas'
-
-const { t } = useI18n() // 国际化
-
-// 表单校验
-export const rules = reactive({
-  name: [required]
-})
-
-// CrudSchema
-const crudSchemas = reactive<VxeCrudSchema>({
-  primaryKey: 'id',
-  primaryType: 'id',
-  primaryTitle: '表单编号',
-  action: true,
-  columns: [
-    {
-      title: '表单名',
-      field: 'name',
-      isSearch: true
-    },
-    {
-      title: t('common.status'),
-      field: 'status',
-      dictType: DICT_TYPE.COMMON_STATUS,
-      dictClass: 'number'
-    },
-    {
-      title: '备注',
-      field: 'remark'
-    },
-    {
-      title: t('common.createTime'),
-      field: 'createTime',
-      formatter: 'formatDate',
-      isForm: false,
-      table: {
-        width: 180
-      }
-    }
-  ]
-})
-export const { allSchemas } = useVxeCrudSchemas(crudSchemas)

+ 0 - 159
src/views/bpm/form/formEditor.vue

@@ -1,159 +0,0 @@
-<template>
-  <ContentWrap>
-    <!-- 表单设计器 -->
-    <fc-designer ref="designer" height="780px">
-      <template #handle>
-        <XButton type="primary" title="生成JSON" @click="showJson" />
-        <XButton type="primary" title="生成Options" @click="showOption" />
-        <XButton type="primary" :title="t('action.save')" @click="handleSave" />
-      </template>
-    </fc-designer>
-    <Dialog :title="dialogTitle" v-model="dialogVisible1" maxHeight="600">
-      <div ref="editor" v-if="dialogVisible1">
-        <XTextButton style="float: right" :title="t('common.copy')" @click="copy(formValue)" />
-        <el-scrollbar height="580">
-          <div v-highlight>
-            <code class="hljs">
-              {{ formValue }}
-            </code>
-          </div>
-        </el-scrollbar>
-      </div>
-    </Dialog>
-    <!-- 表单保存的弹窗 -->
-    <XModal v-model="dialogVisible" title="保存表单">
-      <el-form ref="formRef" :model="formValues" :rules="formRules" label-width="80px">
-        <el-form-item label="表单名" prop="name">
-          <el-input v-model="formValues.name" placeholder="请输入表单名" />
-        </el-form-item>
-        <el-form-item label="开启状态" prop="status">
-          <el-radio-group v-model="formValues.status">
-            <el-radio
-              v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
-              :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="formValues.remark" type="textarea" placeholder="请输入备注" />
-        </el-form-item>
-      </el-form>
-      <!-- 操作按钮 -->
-      <template #footer>
-        <!-- 按钮:保存 -->
-        <XButton
-          type="primary"
-          :title="t('action.save')"
-          :loading="dialogLoading"
-          @click="submitForm"
-        />
-        <!-- 按钮:关闭 -->
-        <XButton :title="t('dialog.close')" @click="dialogVisible = false" />
-      </template>
-    </XModal>
-  </ContentWrap>
-</template>
-<script setup lang="ts" name="BpmFormEditor">
-import { FormInstance } from 'element-plus'
-import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
-import { CommonStatusEnum } from '@/utils/constants'
-import * as FormApi from '@/api/bpm/form'
-import { encodeConf, encodeFields, setConfAndFields } from '@/utils/formCreate'
-import { useClipboard } from '@vueuse/core'
-
-const { t } = useI18n() // 国际化
-const message = useMessage() // 消息
-const { query } = useRoute() // 路由
-
-const designer = ref() // 表单设计器
-const type = ref(-1)
-const formValue = ref('')
-const dialogTitle = ref('')
-const dialogVisible = ref(false) // 弹窗是否展示
-const dialogVisible1 = ref(false) // 弹窗是否展示
-const dialogLoading = ref(false) // 弹窗的加载中
-const formRef = ref<FormInstance>()
-const formRules = reactive({
-  name: [{ required: true, message: '表单名不能为空', trigger: 'blur' }],
-  status: [{ required: true, message: '开启状态不能为空', trigger: 'blur' }]
-})
-const formValues = ref({
-  name: '',
-  status: CommonStatusEnum.ENABLE,
-  remark: ''
-})
-
-// 处理保存按钮
-const handleSave = () => {
-  dialogVisible.value = true
-}
-
-// 提交保存表单
-const submitForm = async () => {
-  // 参数校验
-  const elForm = unref(formRef)
-  if (!elForm) return
-  const valid = await elForm.validate()
-  if (!valid) return
-
-  // 提交请求
-  dialogLoading.value = true
-  try {
-    const data = formValues.value as FormApi.FormVO
-    data.conf = encodeConf(designer) // 表单配置
-    data.fields = encodeFields(designer) // 表单字段
-    if (!data.id) {
-      await FormApi.createFormApi(data)
-      message.success(t('common.createSuccess'))
-    } else {
-      await FormApi.updateFormApi(data)
-      message.success(t('common.updateSuccess'))
-    }
-    dialogVisible.value = false
-  } finally {
-    dialogLoading.value = false
-  }
-}
-const showJson = () => {
-  openModel('生成JSON')
-  type.value = 0
-  formValue.value = designer.value.getRule()
-}
-const showOption = () => {
-  openModel('生成Options')
-  type.value = 1
-  formValue.value = designer.value.getOption()
-}
-const openModel = (title: string) => {
-  dialogVisible1.value = true
-  dialogTitle.value = title
-}
-/** 复制 **/
-const copy = async (text: string) => {
-  const { copy, copied, isSupported } = useClipboard({ source: text })
-  if (!isSupported) {
-    message.error(t('common.copyError'))
-  } else {
-    await copy()
-    if (unref(copied)) {
-      message.success(t('common.copySuccess'))
-    }
-  }
-}
-// ========== 初始化 ==========
-onMounted(() => {
-  // 场景一:新增表单
-  const id = query.id as unknown as number
-  if (!id) {
-    return
-  }
-  // 场景二:修改表单
-  FormApi.getFormApi(id).then((data) => {
-    formValues.value = data
-    setConfAndFields(designer, data.conf, data.fields)
-  })
-})
-</script>

+ 144 - 66
src/views/bpm/form/index.vue

@@ -1,93 +1,171 @@
 <template>
-  <ContentWrap>
-    <!-- 列表 -->
-    <XTable @register="registerTable">
-      <!-- 操作:新增 -->
-      <template #toolbar_buttons>
-        <XButton
-          type="primary"
-          preIcon="ep:zoom-in"
-          :title="t('action.add')"
-          v-hasPermi="['system:post:create']"
-          @click="handleCreate()"
+  <content-wrap>
+    <!-- 搜索工作栏 -->
+    <el-form
+      class="-mb-15px"
+      :model="queryParams"
+      ref="queryFormRef"
+      :inline="true"
+      label-width="68px"
+    >
+      <el-form-item label="表单名" prop="name">
+        <el-input
+          v-model="queryParams.name"
+          placeholder="请输入表单名"
+          clearable
+          @keyup.enter="handleQuery"
+          class="!w-240px"
         />
-      </template>
-      <template #actionbtns_default="{ row }">
-        <!-- 操作:修改 -->
-        <XTextButton
-          preIcon="ep:edit"
-          :title="t('action.edit')"
-          v-hasPermi="['bpm:form:update']"
-          @click="handleUpdate(row.id)"
-        />
-        <!-- 操作:详情 -->
-        <XTextButton
-          preIcon="ep:view"
-          :title="t('action.detail')"
-          v-hasPermi="['bpm:form:query']"
-          @click="handleDetail(row.id)"
-        />
-        <!-- 操作:删除 -->
-        <XTextButton
-          preIcon="ep:delete"
-          :title="t('action.del')"
-          v-hasPermi="['bpm:form:delete']"
-          @click="deleteData(row.id)"
-        />
-      </template>
-    </XTable>
-    <!-- 表单详情的弹窗 -->
-    <XModal v-model="detailOpen" width="800" title="表单详情">
-      <form-create :rule="detailPreview.rule" :option="detailPreview.option" v-if="detailOpen" />
-    </XModal>
-  </ContentWrap>
+      </el-form-item>
+      <el-form-item>
+        <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
+        <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
+        <el-button type="primary" @click="openForm()" v-hasPermi="['bpm:form:create']">
+          <Icon icon="ep:plus" class="mr-5px" /> 新增
+        </el-button>
+      </el-form-item>
+    </el-form>
+  </content-wrap>
+
+  <!-- 列表 -->
+  <content-wrap>
+    <el-table v-loading="loading" :data="list">
+      <el-table-column label="编号" align="center" prop="id" />
+      <el-table-column label="表单名" align="center" prop="name" />
+      <el-table-column label="状态" align="center" prop="status">
+        <template #default="scope">
+          <dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
+        </template>
+      </el-table-column>
+      <el-table-column label="备注" align="center" prop="remark" />
+      <el-table-column
+        label="创建时间"
+        align="center"
+        prop="createTime"
+        :formatter="dateFormatter"
+      />
+      <el-table-column label="操作" align="center">
+        <template #default="scope">
+          <el-button
+            link
+            type="primary"
+            @click="openForm(scope.row.id)"
+            v-hasPermi="['bpm:form:update']"
+          >
+            编辑
+          </el-button>
+          <el-button link @click="openDetail(scope.row.id)" v-hasPermi="['bpm:form:query']">
+            详情
+          </el-button>
+          <el-button
+            link
+            type="danger"
+            @click="handleDelete(scope.row.id)"
+            v-hasPermi="['bpm:form:delete']"
+          >
+            删除
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 分页 -->
+    <Pagination
+      :total="total"
+      v-model:page="queryParams.pageNo"
+      v-model:limit="queryParams.pageSize"
+      @pagination="getList"
+    />
+  </content-wrap>
+
+  <!-- 表单详情的弹窗 -->
+  <Dialog title="表单详情" v-model="detailVisible" width="800">
+    <form-create :rule="detailData.rule" :option="detailData.option" />
+  </Dialog>
 </template>
 
-<script setup lang="ts" name="BpmForm">
-// 业务相关的 import
+<script setup lang="ts" name="Form">
+import { DICT_TYPE } from '@/utils/dict'
+import { dateFormatter } from '@/utils/formatTime'
 import * as FormApi from '@/api/bpm/form'
-import { allSchemas } from './form.data'
-// 表单详情相关的变量和 import
 import { setConfAndFields2 } from '@/utils/formCreate'
 
+const message = useMessage() // 消息弹窗
 const { t } = useI18n() // 国际化
 const { push } = useRouter() // 路由
 
-// 列表相关的变量
-const [registerTable, { deleteData }] = useXTable({
-  allSchemas: allSchemas,
-  getListApi: FormApi.getFormPageApi,
-  deleteApi: FormApi.deleteFormApi
+const loading = ref(true) // 列表的加载中
+const total = ref(0) // 列表的总页数
+const list = ref([]) // 列表的数据
+const queryParams = reactive({
+  pageNo: 1,
+  pageSize: 10,
+  name: null
 })
+const queryFormRef = ref() // 搜索的表单
 
-// 新增操作
-const handleCreate = () => {
-  push({
-    name: 'bpmFormEditor'
-  })
+/** 查询列表 */
+const getList = async () => {
+  loading.value = true
+  try {
+    const data = await FormApi.getFormPage(queryParams)
+    list.value = data.list
+    total.value = data.total
+  } finally {
+    loading.value = false
+  }
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.pageNo = 1
+  getList()
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value.resetFields()
+  handleQuery()
 }
 
-// 修改操作
-const handleUpdate = async (rowId: number) => {
-  await push({
+/** 添加/修改操作 */
+const openForm = (id?: number) => {
+  push({
     name: 'bpmFormEditor',
     query: {
-      id: rowId
+      id
     }
   })
 }
 
-// 详情操作
-const detailOpen = ref(false)
-const detailPreview = ref({
+/** 删除按钮操作 */
+const handleDelete = async (id: number) => {
+  try {
+    // 删除的二次确认
+    await message.delConfirm()
+    // 发起删除
+    await FormApi.deleteForm(id)
+    message.success(t('common.delSuccess'))
+    // 刷新列表
+    await getList()
+  } catch {}
+}
+
+/** 详情操作 */
+const detailVisible = ref(false)
+const detailData = ref({
   rule: [],
   option: {}
 })
-const handleDetail = async (rowId: number) => {
+const openDetail = async (rowId: number) => {
   // 设置表单
-  const data = await FormApi.getFormApi(rowId)
-  setConfAndFields2(detailPreview, data.conf, data.fields)
+  const data = await FormApi.getForm(rowId)
+  setConfAndFields2(detailData, data.conf, data.fields)
   // 弹窗打开
-  detailOpen.value = true
+  detailVisible.value = true
 }
+
+/** 初始化 **/
+onMounted(() => {
+  getList()
+})
 </script>

+ 130 - 0
src/views/bpm/group/UserGroupForm.vue

@@ -0,0 +1,130 @@
+<template>
+  <Dialog :title="modelTitle" v-model="modelVisible">
+    <el-form
+      ref="formRef"
+      :model="formData"
+      :rules="formRules"
+      label-width="100px"
+      v-loading="formLoading"
+    >
+      <el-form-item label="组名" prop="name">
+        <el-input v-model="formData.name" placeholder="请输入组名" />
+      </el-form-item>
+      <el-form-item label="描述">
+        <el-input type="textarea" v-model="formData.name" placeholder="请输入描述" />
+      </el-form-item>
+      <el-form-item label="成员" prop="memberUserIds">
+        <el-select v-model="formData.memberUserIds" multiple placeholder="请选择成员">
+          <el-option
+            v-for="user in userList"
+            :key="user.id"
+            :label="user.nickname"
+            :value="user.id"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="状态" prop="status">
+        <el-radio-group v-model="formData.status">
+          <el-radio
+            v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
+            :key="dict.value"
+            :label="dict.value"
+          >
+            {{ dict.label }}
+          </el-radio>
+        </el-radio-group>
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
+      <el-button @click="modelVisible = false">取 消</el-button>
+    </template>
+  </Dialog>
+</template>
+<script setup lang="ts">
+import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
+import { CommonStatusEnum } from '@/utils/constants'
+import * as UserGroupApi from '@/api/bpm/userGroup'
+import * as UserApi from '@/api/system/user'
+
+const { t } = useI18n() // 国际化
+const message = useMessage() // 消息弹窗
+
+const modelVisible = ref(false) // 弹窗的是否展示
+const modelTitle = ref('') // 弹窗的标题
+const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
+const formType = ref('') // 表单的类型:create - 新增;update - 修改
+const formData = ref({
+  id: undefined,
+  name: undefined,
+  description: undefined,
+  memberUserIds: undefined,
+  status: CommonStatusEnum.ENABLE
+})
+const formRules = reactive({
+  name: [{ required: true, message: '组名不能为空', trigger: 'blur' }],
+  description: [{ required: true, message: '描述不能为空', trigger: 'blur' }],
+  memberUserIds: [{ required: true, message: '成员不能为空', trigger: 'blur' }],
+  status: [{ required: true, message: '状态不能为空', trigger: 'blur' }]
+})
+const formRef = ref() // 表单 Ref
+const userList = ref([]) // 用户列表
+
+/** 打开弹窗 */
+const open = async (type: string, id?: number) => {
+  modelVisible.value = true
+  modelTitle.value = t('action.' + type)
+  formType.value = type
+  resetForm()
+  // 修改时,设置数据
+  if (id) {
+    formLoading.value = true
+    try {
+      formData.value = await UserGroupApi.getUserGroup(id)
+    } finally {
+      formLoading.value = false
+    }
+  }
+  // 加载用户列表
+  userList.value = await UserApi.getSimpleUserList()
+}
+defineExpose({ open }) // 提供 open 方法,用于打开弹窗
+
+/** 提交表单 */
+const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
+const submitForm = async () => {
+  // 校验表单
+  if (!formRef) return
+  const valid = await formRef.value.validate()
+  if (!valid) return
+  // 提交请求
+  formLoading.value = true
+  try {
+    const data = formData.value as unknown as UserGroupApi.UserGroupVO
+    if (formType.value === 'create') {
+      await UserGroupApi.createUserGroup(data)
+      message.success(t('common.createSuccess'))
+    } else {
+      await UserGroupApi.updateUserGroup(data)
+      message.success(t('common.updateSuccess'))
+    }
+    modelVisible.value = false
+    // 发送操作成功的事件
+    emit('success')
+  } finally {
+    formLoading.value = false
+  }
+}
+
+/** 重置表单 */
+const resetForm = () => {
+  formData.value = {
+    id: undefined,
+    name: undefined,
+    description: undefined,
+    memberUserIds: undefined,
+    status: CommonStatusEnum.ENABLE
+  }
+  formRef.value?.resetFields()
+}
+</script>

+ 0 - 64
src/views/bpm/group/group.data.ts

@@ -1,64 +0,0 @@
-import type { VxeCrudSchema } from '@/hooks/web/useVxeCrudSchemas'
-
-const { t } = useI18n() // 国际化
-
-// 表单校验
-export const rules = reactive({
-  name: [required],
-  description: [required],
-  memberUserIds: [required],
-  status: [required]
-})
-
-// CrudSchema
-const crudSchemas = reactive<VxeCrudSchema>({
-  primaryKey: 'id',
-  primaryType: 'id',
-  primaryTitle: '编号',
-  action: true,
-  searchSpan: 8,
-  columns: [
-    {
-      title: '组名',
-      field: 'name',
-      isSearch: true
-    },
-    {
-      title: '成员',
-      field: 'memberUserIds',
-      table: {
-        slots: {
-          default: 'memberUserIds_default'
-        }
-      }
-    },
-    {
-      title: '描述',
-      field: 'description'
-    },
-    {
-      title: t('common.status'),
-      field: 'status',
-      dictType: DICT_TYPE.COMMON_STATUS,
-      dictClass: 'number',
-      isSearch: true
-    },
-    {
-      title: t('common.createTime'),
-      field: 'createTime',
-      formatter: 'formatDate',
-      isForm: false,
-      isSearch: true,
-      search: {
-        show: true,
-        itemRender: {
-          name: 'XDataTimePicker'
-        }
-      },
-      table: {
-        width: 180
-      }
-    }
-  ]
-})
-export const { allSchemas } = useVxeCrudSchemas(crudSchemas)

+ 158 - 156
src/views/bpm/group/index.vue

@@ -1,182 +1,184 @@
 <template>
   <ContentWrap>
-    <!-- 列表 -->
-    <XTable @register="registerTable">
-      <template #toolbar_buttons>
-        <!-- 操作:新增 -->
-        <XButton
-          type="primary"
-          preIcon="ep:zoom-in"
-          :title="t('action.add')"
-          v-hasPermi="['bpm:user-group:create']"
-          @click="handleCreate()"
-        />
-      </template>
-      <template #memberUserIds_default="{ row }">
-        <span v-for="userId in row.memberUserIds" :key="userId">
-          {{ getUserNickname(userId) }} &nbsp;
-        </span>
-      </template>
-      <template #actionbtns_default="{ row }">
-        <!-- 操作:修改 -->
-        <XTextButton
-          preIcon="ep:edit"
-          :title="t('action.edit')"
-          v-hasPermi="['bpm:user-group:update']"
-          @click="handleUpdate(row.id)"
-        />
-        <!-- 操作:详情 -->
-        <XTextButton
-          preIcon="ep:view"
-          :title="t('action.detail')"
-          v-hasPermi="['bpm:user-group:query']"
-          @click="handleDetail(row.id)"
+    <!-- 搜索工作栏 -->
+    <el-form
+      class="-mb-15px"
+      :model="queryParams"
+      ref="queryFormRef"
+      :inline="true"
+      label-width="68px"
+    >
+      <el-form-item label="组名" prop="name">
+        <el-input
+          v-model="queryParams.name"
+          placeholder="请输入组名"
+          clearable
+          @keyup.enter="handleQuery"
+          class="!w-240px"
         />
-        <!-- 操作:删除 -->
-        <XTextButton
-          preIcon="ep:delete"
-          :title="t('action.del')"
-          v-hasPermi="['bpm:user-group:delete']"
-          @click="deleteData(row.id)"
+      </el-form-item>
+      <el-form-item label="状态" prop="status">
+        <el-select v-model="queryParams.status" placeholder="请选择状态" clearable class="!w-240px">
+          <el-option
+            v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
+            :key="dict.value"
+            :label="dict.label"
+            :value="dict.value"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="创建时间" prop="createTime">
+        <el-date-picker
+          v-model="queryParams.createTime"
+          value-format="yyyy-MM-dd HH:mm:ss"
+          type="daterange"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+          :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
+          class="!w-240px"
         />
-      </template>
-    </XTable>
+      </el-form-item>
+      <el-form-item>
+        <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
+        <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
+        <el-button
+          type="primary"
+          @click="openForm('create')"
+          v-hasPermi="['bpm:user-group:create']"
+        >
+          <Icon icon="ep:plus" class="mr-5px" /> 新增
+        </el-button>
+      </el-form-item>
+    </el-form>
   </ContentWrap>
 
-  <XModal v-model="dialogVisible" :title="dialogTitle" :mask-closable="false">
-    <!-- 对话框(添加 / 修改) -->
-    <Form
-      v-if="['create', 'update'].includes(actionType)"
-      :schema="allSchemas.formSchema"
-      :rules="rules"
-      ref="formRef"
-    >
-      <template #memberUserIds="form">
-        <el-select v-model="form.memberUserIds" multiple>
-          <el-option v-for="item in users" :key="item.id" :label="item.nickname" :value="item.id" />
-        </el-select>
-      </template>
-    </Form>
-    <!-- 对话框(详情) -->
-    <Descriptions
-      v-if="actionType === 'detail'"
-      :schema="allSchemas.detailSchema"
-      :data="detailData"
-    >
-      <template #memberUserIds="{ row }">
-        <span v-for="userId in row.memberUserIds" :key="userId">
-          {{ getUserNickname(userId) }} &nbsp;
-        </span>
-      </template>
-    </Descriptions>
-    <!-- 操作按钮 -->
-    <template #footer>
-      <!-- 按钮:保存 -->
-      <XButton
-        v-if="['create', 'update'].includes(actionType)"
-        type="primary"
-        :title="t('action.save')"
-        :loading="actionLoading"
-        @click="submitForm"
+  <!-- 列表 -->
+  <ContentWrap>
+    <el-table v-loading="loading" :data="list">
+      <el-table-column label="编号" align="center" prop="id" />
+      <el-table-column label="组名" align="center" prop="name" />
+      <el-table-column label="描述" align="center" prop="description" />
+      <el-table-column label="成员" align="center">
+        <template #default="scope">
+          <span v-for="userId in scope.row.memberUserIds" :key="userId" class="pr-5px">
+            {{ userList.find((user) => user.id === userId)?.nickname }}
+          </span>
+        </template>
+      </el-table-column>
+      <el-table-column label="状态" align="center" prop="status">
+        <template #default="scope">
+          <dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
+        </template>
+      </el-table-column>
+      <el-table-column
+        label="创建时间"
+        align="center"
+        prop="createTime"
+        :formatter="dateFormatter"
       />
-      <!-- 按钮:关闭 -->
-      <XButton :loading="actionLoading" :title="t('dialog.close')" @click="dialogVisible = false" />
-    </template>
-  </XModal>
+      <el-table-column label="操作" align="center">
+        <template #default="scope">
+          <el-button
+            link
+            type="primary"
+            @click="openForm('update', scope.row.id)"
+            v-hasPermi="['bpm:user-group:update']"
+          >
+            编辑
+          </el-button>
+          <el-button
+            link
+            type="danger"
+            @click="handleDelete(scope.row.id)"
+            v-hasPermi="['bpm:user-group:delete']"
+          >
+            删除
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 分页 -->
+    <Pagination
+      :total="total"
+      v-model:page="queryParams.pageNo"
+      v-model:limit="queryParams.pageSize"
+      @pagination="getList"
+    />
+  </ContentWrap>
+
+  <!-- 表单弹窗:添加/修改 -->
+  <UserGroupForm ref="formRef" @success="getList" />
 </template>
 
-<script setup lang="ts">
-// 业务相关的 import
+<script setup lang="ts" name="UserGroup">
+import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
+import { dateFormatter } from '@/utils/formatTime'
 import * as UserGroupApi from '@/api/bpm/userGroup'
-import { getListSimpleUsersApi, UserVO } from '@/api/system/user'
-import { allSchemas, rules } from './group.data'
-import { FormExpose } from '@/components/Form'
-
-const { t } = useI18n() // 国际化
+import * as UserApi from '@/api/system/user'
+import UserGroupForm from './UserGroupForm.vue'
 const message = useMessage() // 消息弹窗
-// 列表相关的变量
-const [registerTable, { reload, deleteData }] = useXTable({
-  allSchemas: allSchemas,
-  getListApi: UserGroupApi.getUserGroupPageApi,
-  deleteApi: UserGroupApi.deleteUserGroupApi
+const { t } = useI18n() // 国际化
+
+const loading = ref(true) // 列表的加载中
+const total = ref(0) // 列表的总页数
+const list = ref([]) // 列表的数据
+const queryParams = reactive({
+  pageNo: 1,
+  pageSize: 10,
+  name: null,
+  status: null,
+  createTime: []
 })
-// 用户列表
-const users = ref<UserVO[]>([])
+const queryFormRef = ref() // 搜索的表单
+const userList = ref([]) // 用户列表
 
-const getUserNickname = (userId) => {
-  for (const user of users.value) {
-    if (user.id === userId) {
-      return user.nickname
-    }
+/** 查询列表 */
+const getList = async () => {
+  loading.value = true
+  try {
+    const data = await UserGroupApi.getUserGroupPage(queryParams)
+    list.value = data.list
+    total.value = data.total
+  } finally {
+    loading.value = false
   }
-  return '未知(' + userId + ')'
-}
-
-// ========== CRUD 相关 ==========
-const actionLoading = ref(false) // 遮罩层
-const actionType = ref('') // 操作按钮的类型
-const dialogVisible = ref(false) // 是否显示弹出层
-const dialogTitle = ref('edit') // 弹出层标题
-const formRef = ref<FormExpose>() // 表单 Ref
-const detailData = ref() // 详情 Ref
-
-// 设置标题
-const setDialogTile = (type: string) => {
-  dialogTitle.value = t('action.' + type)
-  actionType.value = type
-  dialogVisible.value = true
 }
 
-// 新增操作
-const handleCreate = () => {
-  setDialogTile('create')
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.pageNo = 1
+  getList()
 }
 
-// 修改操作
-const handleUpdate = async (rowId: number) => {
-  setDialogTile('update')
-  // 设置数据
-  const res = await UserGroupApi.getUserGroupApi(rowId)
-  unref(formRef)?.setValues(res)
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value.resetFields()
+  handleQuery()
 }
 
-// 详情操作
-const handleDetail = async (rowId: number) => {
-  setDialogTile('detail')
-  detailData.value = await UserGroupApi.getUserGroupApi(rowId)
+/** 添加/修改操作 */
+const formRef = ref()
+const openForm = (type: string, id?: number) => {
+  formRef.value.open(type, id)
 }
 
-// 提交按钮
-const submitForm = async () => {
-  const elForm = unref(formRef)?.getElFormRef()
-  if (!elForm) return
-  elForm.validate(async (valid) => {
-    if (valid) {
-      actionLoading.value = true
-      // 提交请求
-      try {
-        const data = unref(formRef)?.formModel as UserGroupApi.UserGroupVO
-        if (actionType.value === 'create') {
-          await UserGroupApi.createUserGroupApi(data)
-          message.success(t('common.createSuccess'))
-        } else {
-          await UserGroupApi.updateUserGroupApi(data)
-          message.success(t('common.updateSuccess'))
-        }
-        dialogVisible.value = false
-      } finally {
-        actionLoading.value = false
-        // 刷新列表
-        await reload()
-      }
-    }
-  })
+/** 删除按钮操作 */
+const handleDelete = async (id: number) => {
+  try {
+    // 删除的二次确认
+    await message.delConfirm()
+    // 发起删除
+    await UserGroupApi.deleteUserGroup(id)
+    message.success(t('common.delSuccess'))
+    // 刷新列表
+    await getList()
+  } catch {}
 }
 
-// ========== 初始化 ==========
-onMounted(() => {
-  getListSimpleUsersApi().then((data) => {
-    users.value = data
-  })
+/** 初始化 **/
+onMounted(async () => {
+  await getList()
+  // 加载用户列表
+  userList.value = await UserApi.getSimpleUserList()
 })
 </script>

+ 227 - 0
src/views/bpm/model/ModelForm.vue

@@ -0,0 +1,227 @@
+<template>
+  <Dialog :title="modelTitle" v-model="modelVisible" width="600">
+    <el-form
+      ref="formRef"
+      :model="formData"
+      :rules="formRules"
+      label-width="110px"
+      v-loading="formLoading"
+    >
+      <el-form-item label="流程标识" prop="key">
+        <el-input
+          v-model="formData.key"
+          placeholder="请输入流标标识"
+          style="width: 330px"
+          :disabled="!!formData.id"
+        />
+        <el-tooltip
+          v-if="!formData.id"
+          class="item"
+          effect="light"
+          content="新建后,流程标识不可修改!"
+          placement="top"
+        >
+          <i style="padding-left: 5px" class="el-icon-question"></i>
+        </el-tooltip>
+        <el-tooltip v-else class="item" effect="light" content="流程标识不可修改!" placement="top">
+          <i style="padding-left: 5px" class="el-icon-question"></i>
+        </el-tooltip>
+      </el-form-item>
+      <el-form-item label="流程名称" prop="name">
+        <el-input
+          v-model="formData.name"
+          placeholder="请输入流程名称"
+          :disabled="!!formData.id"
+          clearable
+        />
+      </el-form-item>
+      <el-form-item v-if="formData.id" label="流程分类" prop="category">
+        <el-select
+          v-model="formData.category"
+          placeholder="请选择流程分类"
+          clearable
+          style="width: 100%"
+        >
+          <el-option
+            v-for="dict in getIntDictOptions(DICT_TYPE.BPM_MODEL_CATEGORY)"
+            :key="dict.value"
+            :label="dict.label"
+            :value="dict.value"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="流程描述" prop="description">
+        <el-input type="textarea" v-model="formData.description" clearable />
+      </el-form-item>
+      <div v-if="formData.id">
+        <el-form-item label="表单类型" prop="formType">
+          <el-radio-group v-model="formData.formType">
+            <el-radio
+              v-for="dict in getIntDictOptions(DICT_TYPE.BPM_MODEL_FORM_TYPE)"
+              :key="dict.value"
+              :label="dict.value"
+            >
+              {{ dict.label }}
+            </el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item v-if="formData.formType === 10" label="流程表单" prop="formId">
+          <el-select v-model="formData.formId" clearable style="width: 100%">
+            <el-option
+              v-for="form in formList"
+              :key="form.id"
+              :label="form.name"
+              :value="form.id"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item
+          v-if="formData.formType === 20"
+          label="表单提交路由"
+          prop="formCustomCreatePath"
+        >
+          <el-input
+            v-model="formData.formCustomCreatePath"
+            placeholder="请输入表单提交路由"
+            style="width: 330px"
+          />
+          <el-tooltip
+            class="item"
+            effect="light"
+            content="自定义表单的提交路径,使用 Vue 的路由地址,例如说:bpm/oa/leave/create"
+            placement="top"
+          >
+            <i style="padding-left: 5px" class="el-icon-question"></i>
+          </el-tooltip>
+        </el-form-item>
+        <el-form-item
+          v-if="formData.formType === 20"
+          label="表单查看路由"
+          prop="formCustomViewPath"
+        >
+          <el-input
+            v-model="formData.formCustomViewPath"
+            placeholder="请输入表单查看路由"
+            style="width: 330px"
+          />
+          <el-tooltip
+            class="item"
+            effect="light"
+            content="自定义表单的查看路径,使用 Vue 的路由地址,例如说:bpm/oa/leave/view"
+            placement="top"
+          >
+            <i style="padding-left: 5px" class="el-icon-question"></i>
+          </el-tooltip>
+        </el-form-item>
+      </div>
+    </el-form>
+    <template #footer>
+      <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
+      <el-button @click="modelVisible = false">取 消</el-button>
+    </template>
+  </Dialog>
+</template>
+<script setup lang="ts">
+import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
+import { ElMessageBox } from 'element-plus'
+import * as ModelApi from '@/api/bpm/model'
+import * as FormApi from '@/api/bpm/form'
+const { t } = useI18n() // 国际化
+const message = useMessage() // 消息弹窗
+
+const modelVisible = ref(false) // 弹窗的是否展示
+const modelTitle = ref('') // 弹窗的标题
+const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
+const formType = ref('') // 表单的类型:create - 新增;update - 修改
+const formData = ref({
+  formType: 10,
+  name: '',
+  category: undefined,
+  description: '',
+  formId: '',
+  formCustomCreatePath: '',
+  formCustomViewPath: ''
+})
+const formRules = reactive({
+  category: [{ required: true, message: '参数分类不能为空', trigger: 'blur' }],
+  name: [{ required: true, message: '参数名称不能为空', trigger: 'blur' }],
+  key: [{ required: true, message: '参数键名不能为空', trigger: 'blur' }],
+  value: [{ required: true, message: '参数键值不能为空', trigger: 'blur' }],
+  visible: [{ required: true, message: '是否可见不能为空', trigger: 'blur' }]
+})
+const formRef = ref() // 表单 Ref
+const formList = ref([]) // 流程表单的下拉框的数据
+
+/** 打开弹窗 */
+const open = async (type: string, id?: number) => {
+  modelVisible.value = true
+  modelTitle.value = t('action.' + type)
+  formType.value = type
+  resetForm()
+  // 修改时,设置数据
+  if (id) {
+    formLoading.value = true
+    try {
+      formData.value = await ModelApi.getModel(id)
+    } finally {
+      formLoading.value = false
+    }
+  }
+  // 获得流程表单的下拉框的数据
+  formList.value = await FormApi.getSimpleFormList()
+}
+defineExpose({ open }) // 提供 open 方法,用于打开弹窗
+
+/** 提交表单 */
+const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
+const submitForm = async () => {
+  // 校验表单
+  if (!formRef) return
+  const valid = await formRef.value.validate()
+  if (!valid) return
+  // 提交请求
+  formLoading.value = true
+  try {
+    const data = formData.value as unknown as ModelApi.ModelVO
+    if (formType.value === 'create') {
+      await ModelApi.createModel(data)
+      // 提示,引导用户做后续的操作
+      await ElMessageBox.alert(
+        '<strong>新建模型成功!</strong>后续需要执行如下 4 个步骤:' +
+          '<div>1. 点击【修改流程】按钮,配置流程的分类、表单信息</div>' +
+          '<div>2. 点击【设计流程】按钮,绘制流程图</div>' +
+          '<div>3. 点击【分配规则】按钮,设置每个用户任务的审批人</div>' +
+          '<div>4. 点击【发布流程】按钮,完成流程的最终发布</div>' +
+          '另外,每次流程修改后,都需要点击【发布流程】按钮,才能正式生效!!!',
+        '重要提示',
+        {
+          dangerouslyUseHTMLString: true,
+          type: 'success'
+        }
+      )
+    } else {
+      await ModelApi.updateModel(data)
+      message.success(t('common.updateSuccess'))
+    }
+    modelVisible.value = false
+    // 发送操作成功的事件
+    emit('success')
+  } finally {
+    formLoading.value = false
+  }
+}
+
+/** 重置表单 */
+const resetForm = () => {
+  formData.value = {
+    formType: 10,
+    name: '',
+    category: undefined,
+    description: '',
+    formId: '',
+    formCustomCreatePath: '',
+    formCustomViewPath: ''
+  }
+  formRef.value?.resetFields()
+}
+</script>

+ 137 - 0
src/views/bpm/model/ModelImportForm.vue

@@ -0,0 +1,137 @@
+<template>
+  <Dialog title="导入流程" v-model="modelVisible" width="400">
+    <div>
+      <el-upload
+        ref="uploadRef"
+        :action="importUrl"
+        :headers="uploadHeaders"
+        :data="formData"
+        name="bpmnFile"
+        v-model:file-list="fileList"
+        drag
+        :auto-upload="false"
+        accept=".bpmn, .xml"
+        :limit="1"
+        :on-exceed="handleExceed"
+        :on-success="submitFormSuccess"
+        :on-error="submitFormError"
+        :disabled="formLoading"
+      >
+        <Icon class="el-icon--upload" icon="ep:upload-filled" />
+        <div class="el-upload__text"> 将文件拖到此处,或 <em>点击上传</em> </div>
+        <template #tip>
+          <div class="el-upload__tip" style="color: red">
+            提示:仅允许导入“bpm”或“xml”格式文件!
+          </div>
+          <div>
+            <el-form ref="formRef" :model="formData" :rules="formRules" label-width="120px">
+              <el-form-item label="流程标识" prop="key">
+                <el-input
+                  v-model="formData.key"
+                  placeholder="请输入流标标识"
+                  style="width: 250px"
+                />
+              </el-form-item>
+              <el-form-item label="流程名称" prop="name">
+                <el-input v-model="formData.name" placeholder="请输入流程名称" clearable />
+              </el-form-item>
+              <el-form-item label="流程描述" prop="description">
+                <el-input type="textarea" v-model="formData.description" clearable />
+              </el-form-item>
+            </el-form>
+          </div>
+        </template>
+      </el-upload>
+    </div>
+    <template #footer>
+      <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
+      <el-button @click="modelVisible = false">取 消</el-button>
+    </template>
+  </Dialog>
+</template>
+<script setup lang="ts">
+import { getAccessToken, getTenantId } from '@/utils/auth'
+const message = useMessage() // 消息弹窗
+
+const modelVisible = ref(false) // 弹窗的是否展示
+const formLoading = ref(false) // 表单的加载中
+const formData = ref({
+  key: '',
+  name: '',
+  description: ''
+})
+const formRules = reactive({
+  key: [{ required: true, message: '流程标识不能为空', trigger: 'blur' }],
+  name: [{ required: true, message: '流程名称不能为空', trigger: 'blur' }]
+})
+const formRef = ref() // 表单 Ref
+const uploadRef = ref() // 上传 Ref
+const importUrl = import.meta.env.VITE_BASE_URL + import.meta.env.VITE_API_URL + '/bpm/model/import'
+const uploadHeaders = ref() // 上传 Header 头
+const fileList = ref([]) // 文件列表
+
+/** 打开弹窗 */
+const open = async () => {
+  modelVisible.value = true
+  resetForm()
+}
+defineExpose({ open }) // 提供 open 方法,用于打开弹窗
+
+/** 提交表单 */
+const submitForm = async () => {
+  // 校验表单
+  if (!formRef) return
+  const valid = await formRef.value.validate()
+  if (!valid) return
+  if (fileList.value.length == 0) {
+    message.error('请上传文件')
+    return
+  }
+  // 提交请求
+  uploadHeaders.value = {
+    Authorization: 'Bearer ' + getAccessToken(),
+    'tenant-id': getTenantId()
+  }
+  formLoading.value = true
+  uploadRef.value!.submit()
+}
+
+/** 文件上传成功 */
+const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
+const submitFormSuccess = async (response: any) => {
+  if (response.code !== 0) {
+    message.error(response.msg)
+    formLoading.value = false
+    return
+  }
+  // 提示成功
+  message.success('导入流程成功!请点击【设计流程】按钮,进行编辑保存后,才可以进行【发布流程】')
+  // 发送操作成功的事件
+  emit('success')
+}
+
+/** 上传错误提示 */
+const submitFormError = (): void => {
+  message.error('导入流程失败,请您重新上传!')
+  formLoading.value = false
+}
+
+/** 重置表单 */
+const resetForm = () => {
+  // 重置上传状态和文件
+  formLoading.value = false
+  uploadRef.value?.clearFiles()
+  // 重置表单
+  formData.value = {
+    key: '',
+    name: '',
+    description: ''
+  }
+  formRef.value?.resetFields()
+}
+
+/** 文件数超出提示 */
+const handleExceed = (): void => {
+  message.error('最多只能上传一个文件!')
+}
+</script>

+ 102 - 0
src/views/bpm/model/editor/index.vue

@@ -0,0 +1,102 @@
+<template>
+  <ContentWrap>
+    <!-- 流程设计器,负责绘制流程等 -->
+    <my-process-designer
+      key="designer"
+      v-if="xmlString !== undefined"
+      v-model="xmlString"
+      :value="xmlString"
+      v-bind="controlForm"
+      keyboard
+      ref="processDesigner"
+      @init-finished="initModeler"
+      :additionalModel="controlForm.additionalModel"
+      @save="save"
+    />
+    <!-- 流程属性器,负责编辑每个流程节点的属性 -->
+    <my-properties-panel
+      key="penal"
+      :bpmnModeler="modeler"
+      :prefix="controlForm.prefix"
+      class="process-panel"
+      :model="model"
+    />
+  </ContentWrap>
+</template>
+
+<script setup lang="ts">
+// 自定义元素选中时的弹出菜单(修改 默认任务 为 用户任务)
+import CustomContentPadProvider from '@/components/bpmnProcessDesigner/package/designer/plugins/content-pad'
+// 自定义左侧菜单(修改 默认任务 为 用户任务)
+import CustomPaletteProvider from '@/components/bpmnProcessDesigner/package/designer/plugins/palette'
+import * as ModelApi from '@/api/bpm/model'
+
+const router = useRouter() // 路由
+const { query } = useRoute() // 路由的查询
+const message = useMessage() // 国际化
+
+const xmlString = ref(undefined) // BPMN XML
+const modeler = ref(null) // BPMN Modeler
+const controlForm = ref({
+  simulation: true,
+  labelEditing: false,
+  labelVisible: false,
+  prefix: 'flowable',
+  headerButtonSize: 'mini',
+  additionalModel: [CustomContentPadProvider, CustomPaletteProvider]
+})
+const model = ref<ModelApi.ModelVO>() // 流程模型的信息
+
+/** 初始化 modeler */
+const initModeler = (item) => {
+  setTimeout(() => {
+    modeler.value = item
+  }, 10)
+}
+
+/** 添加/修改模型 */
+const save = async (bpmnXml) => {
+  const data = {
+    ...model.value,
+    bpmnXml: bpmnXml // bpmnXml 只是初始化流程图,后续修改无法通过它获得
+  } as unknown as ModelApi.ModelVO
+  // 提交
+  if (data.id) {
+    await ModelApi.updateModel(data)
+    message.success('修改成功')
+  } else {
+    await ModelApi.createModel(data)
+    message.success('新增成功')
+  }
+  // 跳转回去
+  close()
+}
+
+/** 关闭按钮 */
+const close = () => {
+  router.push({ path: '/bpm/manager/model' })
+}
+
+/** 初始化 */
+onMounted(async () => {
+  const modelId = query.modelId as unknown as number
+  if (!modelId) {
+    message.error('缺少模型 modelId 编号')
+    return
+  }
+  // 查询模型
+  const data = await ModelApi.getModel(modelId)
+  xmlString.value = data.bpmnXml
+  model.value = {
+    ...data,
+    bpmnXml: undefined // 清空 bpmnXml 属性
+  }
+})
+</script>
+<style lang="scss">
+.process-panel__container {
+  position: absolute;
+  right: 60px;
+  top: 90px;
+}
+</style>

+ 338 - 535
src/views/bpm/model/index.vue

@@ -1,353 +1,324 @@
 <template>
   <ContentWrap>
-    <!-- 列表 -->
-    <XTable @register="registerTable">
-      <template #toolbar_buttons>
-        <!-- 操作:新增 -->
-        <XButton
-          type="primary"
-          preIcon="ep:zoom-in"
-          title="新建流程"
-          v-hasPermi="['bpm:model:create']"
-          @click="handleCreate"
-        />
-        <!-- 操作:导入 -->
-        <XButton
-          type="warning"
-          preIcon="ep:upload"
-          :title="'导入流程'"
-          @click="handleImport"
-          style="margin-left: 10px"
-        />
-      </template>
-      <!-- 流程名称 -->
-      <template #name_default="{ row }">
-        <XTextButton :title="row.name" @click="handleBpmnDetail(row.id)" />
-      </template>
-      <!-- 流程分类 -->
-      <template #category_default="{ row }">
-        <DictTag :type="DICT_TYPE.BPM_MODEL_CATEGORY" :value="Number(row?.category)" />
-      </template>
-      <!-- 表单信息 -->
-      <template #formId_default="{ row }">
-        <XTextButton
-          v-if="row.formType === 10"
-          :title="forms.find((form) => form.id === row.formId)?.name || row.formId"
-          @click="handleFormDetail(row)"
-        />
-        <XTextButton v-else :title="row.formCustomCreatePath" @click="handleFormDetail(row)" />
-      </template>
-      <!-- 流程版本 -->
-      <template #version_default="{ row }">
-        <el-tag v-if="row.processDefinition">v{{ row.processDefinition.version }}</el-tag>
-        <el-tag type="warning" v-else>未部署</el-tag>
-      </template>
-      <!-- 激活状态 -->
-      <template #status_default="{ row }">
-        <el-switch
-          v-if="row.processDefinition"
-          v-model="row.processDefinition.suspensionState"
-          :active-value="1"
-          :inactive-value="2"
-          @change="handleChangeState(row)"
-        />
-      </template>
-      <!-- 操作 -->
-      <template #actionbtns_default="{ row }">
-        <XTextButton
-          preIcon="ep:edit"
-          title="修改流程"
-          v-hasPermi="['bpm:model:update']"
-          @click="handleUpdate(row.id)"
-        />
-        <XTextButton
-          preIcon="ep:setting"
-          title="设计流程"
-          v-hasPermi="['bpm:model:update']"
-          @click="handleDesign(row)"
-        />
-        <XTextButton
-          preIcon="ep:user"
-          title="分配规则"
-          v-hasPermi="['bpm:task-assign-rule:query']"
-          @click="handleAssignRule(row)"
-        />
-        <XTextButton
-          preIcon="ep:position"
-          title="发布流程"
-          v-hasPermi="['bpm:model:deploy']"
-          @click="handleDeploy(row)"
-        />
-        <XTextButton
-          preIcon="ep:aim"
-          title="流程定义"
-          v-hasPermi="['bpm:process-definition:query']"
-          @click="handleDefinitionList(row)"
+    <!-- 搜索工作栏 -->
+    <el-form
+      class="-mb-15px"
+      :model="queryParams"
+      ref="queryFormRef"
+      :inline="true"
+      label-width="68px"
+    >
+      <el-form-item label="流程标识" prop="key">
+        <el-input
+          v-model="queryParams.key"
+          placeholder="请输入流程标识"
+          clearable
+          @keyup.enter="handleQuery"
+          class="!w-240px"
         />
-        <!-- 操作:删除 -->
-        <XTextButton
-          preIcon="ep:delete"
-          :title="t('action.del')"
-          v-hasPermi="['bpm:model:delete']"
-          @click="handleDelete(row.id)"
+      </el-form-item>
+      <el-form-item label="流程名称" prop="name">
+        <el-input
+          v-model="queryParams.name"
+          placeholder="请输入流程名称"
+          clearable
+          @keyup.enter="handleQuery"
+          class="!w-240px"
         />
-      </template>
-    </XTable>
-
-    <!-- 对话框(添加 / 修改流程) -->
-    <XModal v-model="dialogVisible" :title="dialogTitle" width="600">
-      <el-form
-        :loading="dialogLoading"
-        el-form
-        ref="saveFormRef"
-        :model="saveForm"
-        :rules="rules"
-        label-width="110px"
-      >
-        <el-form-item label="流程标识" prop="key">
-          <el-input
-            v-model="saveForm.key"
-            placeholder="请输入流标标识"
-            style="width: 330px"
-            :disabled="!!saveForm.id"
+      </el-form-item>
+      <el-form-item label="流程分类" prop="category">
+        <el-select
+          v-model="queryParams.category"
+          placeholder="请选择流程分类"
+          clearable
+          class="!w-240px"
+        >
+          <el-option
+            v-for="dict in getIntDictOptions(DICT_TYPE.BPM_MODEL_CATEGORY)"
+            :key="dict.value"
+            :label="dict.label"
+            :value="dict.value"
           />
-          <el-tooltip
-            v-if="!saveForm.id"
-            class="item"
-            effect="light"
-            content="新建后,流程标识不可修改!"
-            placement="top"
-          >
-            <i style="padding-left: 5px" class="el-icon-question"></i>
-          </el-tooltip>
-          <el-tooltip
-            v-else
-            class="item"
-            effect="light"
-            content="流程标识不可修改!"
-            placement="top"
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
+        <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
+        <el-button
+          type="primary"
+          plain
+          @click="openForm('create')"
+          v-hasPermi="['bpm:model:create']"
+        >
+          <Icon icon="ep:plus" class="mr-5px" /> 新建流程
+        </el-button>
+        <el-button type="success" plain @click="openImportForm" v-hasPermi="['bpm:model:import']">
+          <Icon icon="ep:upload" class="mr-5px" /> 导入流程
+        </el-button>
+      </el-form-item>
+    </el-form>
+  </ContentWrap>
+
+  <!-- 列表 -->
+  <ContentWrap>
+    <el-table v-loading="loading" :data="list">
+      <el-table-column label="流程标识" align="center" prop="key" width="200" />
+      <el-table-column label="流程名称" align="center" prop="name" width="200">
+        <template #default="scope">
+          <el-button type="text" @click="handleBpmnDetail(scope.row)">
+            <span>{{ scope.row.name }}</span>
+          </el-button>
+        </template>
+      </el-table-column>
+      <el-table-column label="流程分类" align="center" prop="category" width="100">
+        <template #default="scope">
+          <dict-tag :type="DICT_TYPE.BPM_MODEL_CATEGORY" :value="scope.row.category" />
+        </template>
+      </el-table-column>
+      <el-table-column label="表单信息" align="center" prop="formType" width="200">
+        <template #default="scope">
+          <el-button
+            v-if="scope.row.formType === 10"
+            type="text"
+            @click="handleFormDetail(scope.row)"
           >
-            <i style="padding-left: 5px" class="el-icon-question"></i>
-          </el-tooltip>
-        </el-form-item>
-        <el-form-item label="流程名称" prop="name">
-          <el-input
-            v-model="saveForm.name"
-            placeholder="请输入流程名称"
-            :disabled="!!saveForm.id"
-            clearable
-          />
-        </el-form-item>
-        <el-form-item v-if="saveForm.id" label="流程分类" prop="category">
-          <el-select
-            v-model="saveForm.category"
-            placeholder="请选择流程分类"
-            clearable
-            style="width: 100%"
+            <span>{{ scope.row.formName }}</span>
+          </el-button>
+          <el-button
+            v-else-if="scope.row.formType === 20"
+            type="text"
+            @click="handleFormDetail(scope.row)"
           >
-            <el-option
-              v-for="(dict, index) in getDictOptions(DICT_TYPE.BPM_MODEL_CATEGORY)"
-              :key="index"
-              :label="dict.label"
-              :value="dict.value"
+            <span>{{ scope.row.formCustomCreatePath }}</span>
+          </el-button>
+          <label v-else>暂无表单</label>
+        </template>
+      </el-table-column>
+      <el-table-column
+        label="创建时间"
+        align="center"
+        prop="createTime"
+        width="180"
+        :formatter="dateFormatter"
+      />
+      <el-table-column label="最新部署的流程定义" align="center">
+        <el-table-column
+          label="流程版本"
+          align="center"
+          prop="processDefinition.version"
+          width="100"
+        >
+          <template #default="scope">
+            <el-tag v-if="scope.row.processDefinition">
+              v{{ scope.row.processDefinition.version }}
+            </el-tag>
+            <el-tag v-else type="warning">未部署</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column
+          label="激活状态"
+          align="center"
+          prop="processDefinition.version"
+          width="85"
+        >
+          <template #default="scope">
+            <el-switch
+              v-if="scope.row.processDefinition"
+              v-model="scope.row.processDefinition.suspensionState"
+              :active-value="1"
+              :inactive-value="2"
+              @change="handleChangeState(scope.row)"
             />
-          </el-select>
-        </el-form-item>
-        <el-form-item label="流程描述" prop="description">
-          <el-input type="textarea" v-model="saveForm.description" clearable />
-        </el-form-item>
-        <div v-if="saveForm.id">
-          <el-form-item label="表单类型" prop="formType">
-            <el-radio-group v-model="saveForm.formType">
-              <el-radio
-                v-for="dict in getDictOptions(DICT_TYPE.BPM_MODEL_FORM_TYPE)"
-                :key="parseInt(dict.value)"
-                :label="parseInt(dict.value)"
-              >
-                {{ dict.label }}
-              </el-radio>
-            </el-radio-group>
-          </el-form-item>
-          <el-form-item v-if="saveForm.formType === 10" label="流程表单" prop="formId">
-            <el-select v-model="saveForm.formId" clearable style="width: 100%">
-              <el-option v-for="form in forms" :key="form.id" :label="form.name" :value="form.id" />
-            </el-select>
-          </el-form-item>
-          <el-form-item
-            v-if="saveForm.formType === 20"
-            label="表单提交路由"
-            prop="formCustomCreatePath"
+          </template>
+        </el-table-column>
+        <el-table-column label="部署时间" align="center" prop="deploymentTime" width="180">
+          <template #default="scope">
+            <span v-if="scope.row.processDefinition">
+              {{ formatDate(scope.row.processDefinition.deploymentTime) }}
+            </span>
+          </template>
+        </el-table-column>
+      </el-table-column>
+      <el-table-column label="操作" align="center" width="240" fixed="right">
+        <template #default="scope">
+          <el-button
+            link
+            type="primary"
+            @click="openForm('update', scope.row.id)"
+            v-hasPermi="['bpm:model:update']"
           >
-            <el-input
-              v-model="saveForm.formCustomCreatePath"
-              placeholder="请输入表单提交路由"
-              style="width: 330px"
-            />
-            <el-tooltip
-              class="item"
-              effect="light"
-              content="自定义表单的提交路径,使用 Vue 的路由地址,例如说:bpm/oa/leave/create"
-              placement="top"
-            >
-              <i style="padding-left: 5px" class="el-icon-question"></i>
-            </el-tooltip>
-          </el-form-item>
-          <el-form-item
-            v-if="saveForm.formType === 20"
-            label="表单查看路由"
-            prop="formCustomViewPath"
+            修改流程
+          </el-button>
+          <el-button
+            link
+            type="primary"
+            @click="handleDesign(scope.row)"
+            v-hasPermi="['bpm:model:update']"
           >
-            <el-input
-              v-model="saveForm.formCustomViewPath"
-              placeholder="请输入表单查看路由"
-              style="width: 330px"
-            />
-            <el-tooltip
-              class="item"
-              effect="light"
-              content="自定义表单的查看路径,使用 Vue 的路由地址,例如说:bpm/oa/leave/view"
-              placement="top"
-            >
-              <i style="padding-left: 5px" class="el-icon-question"></i>
-            </el-tooltip>
-          </el-form-item>
-        </div>
-      </el-form>
-      <template #footer>
-        <!-- 按钮:保存 -->
-        <XButton
-          type="primary"
-          :loading="dialogLoading"
-          @click="submitForm"
-          :title="t('action.save')"
-        />
-        <!-- 按钮:关闭 -->
-        <XButton
-          :loading="dialogLoading"
-          @click="dialogVisible = false"
-          :title="t('dialog.close')"
-        />
-      </template>
-    </XModal>
+            设计流程
+          </el-button>
+          <el-button
+            link
+            type="primary"
+            @click="handleAssignRule(scope.row)"
+            v-hasPermi="['bpm:task-assign-rule:query']"
+          >
+            分配规则
+          </el-button>
+          <el-button
+            link
+            type="primary"
+            @click="handleDeploy(scope.row)"
+            v-hasPermi="['bpm:model:deploy']"
+          >
+            发布流程
+          </el-button>
+          <el-button
+            link
+            type="primary"
+            v-hasPermi="['bpm:process-definition:query']"
+            @click="handleDefinitionList(scope.row)"
+          >
+            流程定义
+          </el-button>
+          <el-button
+            link
+            type="danger"
+            @click="handleDelete(scope.row.id)"
+            v-hasPermi="['bpm:model:delete']"
+          >
+            删除
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 分页 -->
+    <Pagination
+      :total="total"
+      v-model:page="queryParams.pageNo"
+      v-model:limit="queryParams.pageSize"
+      @pagination="getList"
+    />
+  </ContentWrap>
 
-    <!-- 导入流程 -->
-    <XModal v-model="importDialogVisible" width="400" title="导入流程">
-      <div>
-        <el-upload
-          ref="uploadRef"
-          :action="importUrl"
-          :headers="uploadHeaders"
-          :drag="true"
-          :limit="1"
-          :multiple="true"
-          :show-file-list="true"
-          :disabled="uploadDisabled"
-          :on-exceed="handleExceed"
-          :on-success="handleFileSuccess"
-          :on-error="excelUploadError"
-          :auto-upload="false"
-          accept=".bpmn, .xml"
-          name="bpmnFile"
-          :data="importForm"
-        >
-          <Icon class="el-icon--upload" icon="ep:upload-filled" />
-          <div class="el-upload__text"> 将文件拖到此处,或 <em>点击上传</em> </div>
-          <template #tip>
-            <div class="el-upload__tip" style="color: red">
-              提示:仅允许导入“bpm”或“xml”格式文件!
-            </div>
-            <div>
-              <el-form
-                ref="importFormRef"
-                :model="importForm"
-                :rules="rules"
-                label-width="120px"
-                status-icon
-              >
-                <el-form-item label="流程标识" prop="key">
-                  <el-input
-                    v-model="importForm.key"
-                    placeholder="请输入流标标识"
-                    style="width: 250px"
-                  />
-                </el-form-item>
-                <el-form-item label="流程名称" prop="name">
-                  <el-input v-model="importForm.name" placeholder="请输入流程名称" clearable />
-                </el-form-item>
-                <el-form-item label="流程描述" prop="description">
-                  <el-input type="textarea" v-model="importForm.description" clearable />
-                </el-form-item>
-              </el-form>
-            </div>
-          </template>
-        </el-upload>
-      </div>
-      <template #footer>
-        <!-- 按钮:保存 -->
-        <XButton
-          type="warning"
-          preIcon="ep:upload-filled"
-          :title="t('action.save')"
-          @click="submitFileForm"
-        />
-        <XButton title="取 消" @click="uploadClose" />
-      </template>
-    </XModal>
+  <!-- 表单弹窗:添加/修改流程 -->
+  <ModelForm ref="formRef" @success="getList" />
 
-    <!-- 表单详情的弹窗 -->
-    <XModal v-model="formDetailVisible" width="800" title="表单详情" :show-footer="false">
-      <form-create
-        :rule="formDetailPreview.rule"
-        :option="formDetailPreview.option"
-        v-if="formDetailVisible"
-      />
-    </XModal>
+  <!-- 表单弹窗:导入流程 -->
+  <ModelImportForm ref="importFormRef" @success="getList" />
 
-    <!-- 流程模型图的预览 -->
-    <XModal title="流程图" v-model="showBpmnOpen" width="80%" height="90%">
-      <my-process-viewer
-        key="designer"
-        v-model="bpmnXML"
-        :value="bpmnXML"
-        v-bind="bpmnControlForm"
-        :prefix="bpmnControlForm.prefix"
-      />
-    </XModal>
-  </ContentWrap>
-</template>
+  <!-- 弹窗:表单详情 -->
+  <Dialog title="表单详情" v-model="formDetailVisible" width="800">
+    <form-create :rule="formDetailPreview.rule" :option="formDetailPreview.option" />
+  </Dialog>
 
-<script setup lang="ts">
-// 全局相关的 import
-import { DICT_TYPE, getDictOptions } from '@/utils/dict'
-import { FormInstance, UploadInstance } from 'element-plus'
+  <!-- 弹窗:流程模型图的预览 -->
+  <Dialog title="流程图" v-model="bpmnDetailVisible" width="800">
+    <my-process-viewer
+      key="designer"
+      v-model="bpmnXML"
+      :value="bpmnXML"
+      v-bind="bpmnControlForm"
+      :prefix="bpmnControlForm.prefix"
+    />
+  </Dialog>
+</template>
 
-// 业务相关的 import
-import { getAccessToken, getTenantId } from '@/utils/auth'
-import * as FormApi from '@/api/bpm/form'
+<script setup lang="ts" name="Form">
+import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
+import { dateFormatter, formatDate } from '@/utils/formatTime'
 import * as ModelApi from '@/api/bpm/model'
-import { allSchemas, rules } from './model.data'
+import * as FormApi from '@/api/bpm/form'
+import ModelForm from './ModelForm.vue'
+import ModelImportForm from '@/views/bpm/model/ModelImportForm.vue'
 import { setConfAndFields2 } from '@/utils/formCreate'
-
-const { t } = useI18n() // 国际化
 const message = useMessage() // 消息弹窗
-const router = useRouter() // 路由
+const { t } = useI18n() // 国际化
+const { push } = useRouter() // 路由
 
-const showBpmnOpen = ref(false)
-const bpmnXML = ref(null)
-const bpmnControlForm = ref({
-  prefix: 'flowable'
-})
-// ========== 列表相关 ==========
-const [registerTable, { reload }] = useXTable({
-  allSchemas: allSchemas,
-  getListApi: ModelApi.getModelPageApi
+const loading = ref(true) // 列表的加载中
+const total = ref(0) // 列表的总页数
+const list = ref([]) // 列表的数据
+const queryParams = reactive({
+  pageNo: 1,
+  pageSize: 10,
+  key: undefined,
+  name: undefined,
+  category: undefined
 })
-const forms = ref() // 流程表单的下拉框的数据
+const queryFormRef = ref() // 搜索的表单
+
+/** 查询列表 */
+const getList = async () => {
+  loading.value = true
+  try {
+    const data = await ModelApi.getModelPage(queryParams)
+    list.value = data.list
+    total.value = data.total
+  } finally {
+    loading.value = false
+  }
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.pageNo = 1
+  getList()
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value.resetFields()
+  handleQuery()
+}
 
-// 设计流程
+/** 添加/修改操作 */
+const formRef = ref()
+const openForm = (type: string, id?: number) => {
+  formRef.value.open(type, id)
+}
+
+/** 添加/修改操作 */
+const importFormRef = ref()
+const openImportForm = () => {
+  importFormRef.value.open()
+}
+
+/** 删除按钮操作 */
+const handleDelete = async (id: number) => {
+  try {
+    // 删除的二次确认
+    await message.delConfirm()
+    // 发起删除
+    await ModelApi.deleteModel(id)
+    message.success(t('common.delSuccess'))
+    // 刷新列表
+    await getList()
+  } catch {}
+}
+
+/** 更新状态操作 */
+const handleChangeState = async (row) => {
+  const state = row.processDefinition.suspensionState
+  try {
+    // 修改状态的二次确认
+    const id = row.id
+    const statusState = state === 1 ? '激活' : '挂起'
+    const content = '是否确认' + statusState + '流程名字为"' + row.name + '"的数据项?'
+    await message.confirm(content)
+    // 发起修改状态
+    await ModelApi.updateModelState(id, state)
+    // 刷新列表
+    await getList()
+  } catch {
+    // 取消后,进行恢复按钮
+    row.processDefinition.suspensionState = state === 1 ? 2 : 1
+  }
+}
+
+/** 设计流程 */
 const handleDesign = (row) => {
-  console.log(row, '设计流程')
-  router.push({
+  push({
     name: 'modelEditor',
     query: {
       modelId: row.id
@@ -355,9 +326,32 @@ const handleDesign = (row) => {
   })
 }
 
-// 跳转到指定流程定义列表
+/** 发布流程 */
+const handleDeploy = async (row) => {
+  try {
+    // 删除的二次确认
+    await message.confirm('是否部署该流程!!')
+    // 发起部署
+    await ModelApi.deployModel(row.id)
+    message.success(t('部署成功'))
+    // 刷新列表
+    await getList()
+  } catch {}
+}
+
+/** 点击任务分配按钮 */
+const handleAssignRule = (row) => {
+  push({
+    name: 'BpmTaskAssignRuleList',
+    query: {
+      modelId: row.id
+    }
+  })
+}
+
+/** 跳转到指定流程定义列表 */
 const handleDefinitionList = (row) => {
-  router.push({
+  push({
     name: 'BpmProcessDefinitionList',
     query: {
       key: row.key
@@ -365,7 +359,7 @@ const handleDefinitionList = (row) => {
   })
 }
 
-// 流程表单的详情按钮操作
+/** 流程表单的详情按钮操作 */
 const formDetailVisible = ref(false)
 const formDetailPreview = ref({
   rule: [],
@@ -374,222 +368,31 @@ const formDetailPreview = ref({
 const handleFormDetail = async (row) => {
   if (row.formType == 10) {
     // 设置表单
-    const data = await FormApi.getFormApi(row.formId)
+    const data = await FormApi.getForm(row.formId)
     setConfAndFields2(formDetailPreview, data.conf, data.fields)
     // 弹窗打开
     formDetailVisible.value = true
   } else {
-    await router.push({
+    await push({
       path: row.formCustomCreatePath
     })
   }
 }
 
-// 流程图的详情按钮操作
-const handleBpmnDetail = (row) => {
-  // TODO 芋艿:流程组件开发中
-  console.log(row)
-  ModelApi.getModelApi(row).then((response) => {
-    console.log(response, 'response')
-    bpmnXML.value = response.bpmnXml
-    // 弹窗打开
-    showBpmnOpen.value = true
-  })
-  // message.success('流程组件开发中,预计 2 月底完成')
-}
-
-// 点击任务分配按钮
-const handleAssignRule = (row) => {
-  router.push({
-    name: 'BpmTaskAssignRuleList',
-    query: {
-      modelId: row.id
-    }
-  })
-}
-
-// ========== 新建/修改流程 ==========
-const dialogVisible = ref(false)
-const dialogTitle = ref('新建模型')
-const dialogLoading = ref(false)
-const saveForm = ref()
-const saveFormRef = ref<FormInstance>()
-
-// 设置标题
-const setDialogTile = async (type: string) => {
-  dialogTitle.value = t('action.' + type)
-  dialogVisible.value = true
-}
-
-// 新增操作
-const handleCreate = async () => {
-  resetForm()
-  await setDialogTile('create')
-}
-
-// 修改操作
-const handleUpdate = async (rowId: number) => {
-  resetForm()
-  await setDialogTile('edit')
-  // 设置数据
-  saveForm.value = await ModelApi.getModelApi(rowId)
-  if (saveForm.value.category == null) {
-    saveForm.value.category = '1'
-  } else {
-    saveForm.value.category = saveForm.value.category
-  }
-}
-
-// 提交按钮
-const submitForm = async () => {
-  // 参数校验
-  const elForm = unref(saveFormRef)
-  if (!elForm) return
-  const valid = await elForm.validate()
-  if (!valid) return
-
-  // 提交请求
-  dialogLoading.value = true
-  try {
-    const data = saveForm.value as ModelApi.ModelVO
-    if (!data.id) {
-      await ModelApi.createModelApi(data)
-      message.success(t('common.createSuccess'))
-    } else {
-      await ModelApi.updateModelApi(data)
-      message.success(t('common.updateSuccess'))
-    }
-    dialogVisible.value = false
-  } finally {
-    // 刷新列表
-    await reload()
-    dialogLoading.value = false
-  }
-}
-
-// 重置表单
-const resetForm = () => {
-  saveForm.value = {
-    formType: 10,
-    name: '',
-    courseSort: '',
-    description: '',
-    formId: '',
-    formCustomCreatePath: '',
-    formCustomViewPath: ''
-  }
-  saveFormRef.value?.resetFields()
-}
-
-// ========== 删除 / 更新状态 / 发布流程 ==========
-// 删除流程
-const handleDelete = (rowId) => {
-  message.delConfirm('是否删除该流程!!').then(async () => {
-    await ModelApi.deleteModelApi(rowId)
-    message.success(t('common.delSuccess'))
-    // 刷新列表
-    reload()
-  })
-}
-
-// 更新状态操作
-const handleChangeState = (row) => {
-  const id = row.id
-  const state = row.processDefinition.suspensionState
-  const statusState = state === 1 ? '激活' : '挂起'
-  const content = '是否确认' + statusState + '流程名字为"' + row.name + '"的数据项?'
-  message
-    .confirm(content)
-    .then(async () => {
-      await ModelApi.updateModelStateApi(id, state)
-      message.success(t('部署成功'))
-      // 刷新列表
-      reload()
-    })
-    .catch(() => {
-      // 取消后,进行恢复按钮
-      row.processDefinition.suspensionState = state === 1 ? 2 : 1
-    })
-}
-
-// 发布流程
-const handleDeploy = (row) => {
-  message.confirm('是否部署该流程!!').then(async () => {
-    await ModelApi.deployModelApi(row.id)
-    message.success(t('部署成功'))
-    // 刷新列表
-    reload()
-  })
-}
-
-// ========== 导入流程 ==========
-const uploadRef = ref<UploadInstance>()
-let importUrl = import.meta.env.VITE_BASE_URL + import.meta.env.VITE_API_URL + '/bpm/model/import'
-const uploadHeaders = ref()
-const importDialogVisible = ref(false)
-const uploadDisabled = ref(false)
-const importFormRef = ref<FormInstance>()
-const importForm = ref({
-  key: '',
-  name: '',
-  description: ''
+/** 流程图的详情按钮操作 */
+const bpmnDetailVisible = ref(false)
+const bpmnXML = ref(null)
+const bpmnControlForm = ref({
+  prefix: 'flowable'
 })
-
-// 导入流程弹窗显示
-const handleImport = () => {
-  importDialogVisible.value = true
-}
-// 文件数超出提示
-const handleExceed = (): void => {
-  message.error('最多只能上传一个文件!')
-}
-// 上传错误提示
-const excelUploadError = (): void => {
-  message.error('导入流程失败,请您重新上传!')
+const handleBpmnDetail = async (row) => {
+  const data = await ModelApi.getModel(row.id)
+  bpmnXML.value = data.bpmnXml || ''
+  bpmnDetailVisible.value = true
 }
 
-// 提交文件上传
-const submitFileForm = () => {
-  uploadHeaders.value = {
-    Authorization: 'Bearer ' + getAccessToken(),
-    'tenant-id': getTenantId()
-  }
-  uploadDisabled.value = true
-  uploadRef.value!.submit()
-}
-// 文件上传成功
-const handleFileSuccess = async (response: any): Promise<void> => {
-  if (response.code !== 0) {
-    message.error(response.msg)
-    return
-  }
-  // 重置表单
-  uploadClose()
-  // 提示,并刷新
-  message.success('导入流程成功!请点击【设计流程】按钮,进行编辑保存后,才可以进行【发布流程】')
-  await reload()
-}
-// 关闭文件上传
-const uploadClose = () => {
-  // 关闭弹窗
-  importDialogVisible.value = false
-  // 重置上传状态和文件
-  uploadDisabled.value = false
-  uploadRef.value!.clearFiles()
-  // 重置表单
-  importForm.value = {
-    key: '',
-    name: '',
-    description: ''
-  }
-  importFormRef.value?.resetFields()
-}
-
-// ========== 初始化 ==========
+/** 初始化 **/
 onMounted(() => {
-  // 获得流程表单的下拉框的数据
-  FormApi.getSimpleFormsApi().then((data) => {
-    forms.value = data
-  })
+  getList()
 })
 </script>

+ 0 - 106
src/views/bpm/model/model.data.ts

@@ -1,106 +0,0 @@
-import type { VxeCrudSchema } from '@/hooks/web/useVxeCrudSchemas'
-
-const { t } = useI18n() // 国际化
-
-// 表单校验
-export const rules = reactive({
-  key: [required],
-  name: [required],
-  category: [required],
-  formType: [required],
-  formId: [required],
-  formCustomCreatePath: [required],
-  formCustomViewPath: [required]
-})
-
-// CrudSchema
-const crudSchemas = reactive<VxeCrudSchema>({
-  primaryKey: 'key',
-  primaryType: null,
-  action: true,
-  actionWidth: '540px',
-  columns: [
-    {
-      title: '流程标识',
-      field: 'key',
-      isSearch: true,
-      table: {
-        width: 120
-      }
-    },
-    {
-      title: '流程名称',
-      field: 'name',
-      isSearch: true,
-      table: {
-        width: 120,
-        slots: {
-          default: 'name_default'
-        }
-      }
-    },
-    {
-      title: '流程分类',
-      field: 'category',
-      dictType: DICT_TYPE.BPM_MODEL_CATEGORY,
-      dictClass: 'number',
-      isSearch: true,
-      table: {
-        slots: {
-          default: 'category_default'
-        }
-      }
-    },
-    {
-      title: '表单信息',
-      field: 'formId',
-      table: {
-        width: 180,
-        slots: {
-          default: 'formId_default'
-        }
-      }
-    },
-    {
-      title: '最新部署的流程定义',
-      field: 'processDefinition',
-      isForm: false,
-      table: {
-        children: [
-          {
-            title: '流程版本',
-            field: 'version',
-            slots: {
-              default: 'version_default'
-            },
-            width: 80
-          },
-          {
-            title: '激活状态',
-            field: 'status',
-            slots: {
-              default: 'status_default'
-            },
-            width: 80
-          },
-          {
-            title: '部署时间',
-            field: 'processDefinition.deploymentTime',
-            formatter: 'formatDate',
-            width: 180
-          }
-        ]
-      }
-    },
-    {
-      title: t('common.createTime'),
-      field: 'createTime',
-      isForm: false,
-      formatter: 'formatDate',
-      table: {
-        width: 180
-      }
-    }
-  ]
-})
-export const { allSchemas } = useVxeCrudSchemas(crudSchemas)

+ 0 - 204
src/views/bpm/model/modelEditor.vue

@@ -1,204 +0,0 @@
-<template>
-  <div class="app-container">
-    <!-- 流程设计器,负责绘制流程等 -->
-    <!-- <myProcessDesigner -->
-    <my-process-designer
-      :key="`designer-${reloadIndex}`"
-      v-if="xmlString !== undefined"
-      v-model="xmlString"
-      :value="xmlString"
-      v-bind="controlForm"
-      keyboard
-      ref="processDesigner"
-      @init-finished="initModeler"
-      :additionalModel="controlForm.additionalModel"
-      @save="save"
-    />
-    <!-- 流程属性器,负责编辑每个流程节点的属性 -->
-    <!-- <MyProcessPalette -->
-    <my-properties-panel
-      :key="`penal-${reloadIndex}`"
-      :bpmnModeler="modeler"
-      :prefix="controlForm.prefix"
-      class="process-panel"
-      :model="model"
-    />
-  </div>
-</template>
-
-<script setup lang="ts">
-// import { translations } from '@/components/bpmnProcessDesigner/src/translations'
-// 自定义元素选中时的弹出菜单(修改 默认任务 为 用户任务)
-import CustomContentPadProvider from '@/components/bpmnProcessDesigner/package/designer/plugins/content-pad'
-// 自定义左侧菜单(修改 默认任务 为 用户任务)
-import CustomPaletteProvider from '@/components/bpmnProcessDesigner/package/designer/plugins/palette'
-// import xmlObj2json from "./utils/xml2json";
-// import myProcessDesigner from '@/components/bpmnProcessDesigner/package/designer/ProcessDesigner.vue'
-// import MyProcessPalette from '@/components/bpmnProcessDesigner/package/palette/ProcessPalette.vue'
-import { createModelApi, getModelApi, updateModelApi, ModelVO } from '@/api/bpm/model'
-
-const router = useRouter()
-const message = useMessage()
-
-// 自定义侧边栏
-// import MyProcessPanel from "../package/process-panel/ProcessPanel";
-
-const xmlString = ref(undefined) // BPMN XML
-const modeler = ref(null)
-const reloadIndex = ref(0)
-// const controlDrawerVisible = ref(false)
-// const translationsSelf = translations
-const controlForm = ref({
-  simulation: true,
-  labelEditing: false,
-  labelVisible: false,
-  prefix: 'flowable',
-  headerButtonSize: 'mini',
-  additionalModel: [CustomContentPadProvider, CustomPaletteProvider]
-})
-// const addis = ref({
-//   CustomContentPadProvider,
-//   CustomPaletteProvider
-// })
-// 流程模型的信息
-const model = ref<ModelVO>()
-onMounted(() => {
-  // 如果 modelId 非空,说明是修改流程模型
-  const modelId = router.currentRoute.value.query && router.currentRoute.value.query.modelId
-  console.log(modelId, 'modelId')
-  if (modelId) {
-    // let data = '4b4909d8-97e7-11ec-8e20-862bc1a4a054'
-    getModelApi(modelId as unknown as number).then((data) => {
-      console.log(data, 'response')
-      xmlString.value = data.bpmnXml
-      model.value = {
-        ...data,
-        bpmnXml: undefined // 清空 bpmnXml 属性
-      }
-      // this.controlForm.processId = data.key
-
-      // xmlString.value =
-      //   '<?xml version="1.0" encoding="UTF-8"?>\n<bpmn2:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="diagram_Process_1645980650311" targetNamespace="http://activiti.org/bpmn"><bpmn2:process id="flowable_01" name="flowable测试" isExecutable="true"><bpmn2:startEvent id="Event_1iruxim"><bpmn2:outgoing>Flow_0804gmo</bpmn2:outgoing></bpmn2:startEvent><bpmn2:userTask id="task01" name="task01"><bpmn2:incoming>Flow_0804gmo</bpmn2:incoming><bpmn2:outgoing>Flow_0cx479x</bpmn2:outgoing></bpmn2:userTask><bpmn2:sequenceFlow id="Flow_0804gmo" sourceRef="Event_1iruxim" targetRef="task01" /><bpmn2:endEvent id="Event_1mdsccz"><bpmn2:incoming>Flow_0cx479x</bpmn2:incoming></bpmn2:endEvent><bpmn2:sequenceFlow id="Flow_0cx479x" sourceRef="task01" targetRef="Event_1mdsccz" /></bpmn2:process><bpmndi:BPMNDiagram id="BPMNDiagram_1"><bpmndi:BPMNPlane id="flowable_01_di" bpmnElement="flowable_01"><bpmndi:BPMNEdge id="Flow_0cx479x_di" bpmnElement="Flow_0cx479x"><di:waypoint x="440" y="350" /><di:waypoint x="492" y="350" /></bpmndi:BPMNEdge><bpmndi:BPMNEdge id="Flow_0804gmo_di" bpmnElement="Flow_0804gmo"><di:waypoint x="288" y="350" /><di:waypoint x="340" y="350" /></bpmndi:BPMNEdge><bpmndi:BPMNShape id="Event_1iruxim_di" bpmnElement="Event_1iruxim"><dc:Bounds x="252" y="332" width="36" height="36" /></bpmndi:BPMNShape><bpmndi:BPMNShape id="task01_di" bpmnElement="task01"><dc:Bounds x="340" y="310" width="100" height="80" /></bpmndi:BPMNShape><bpmndi:BPMNShape id="Event_1mdsccz_di" bpmnElement="Event_1mdsccz"><dc:Bounds x="492" y="332" width="36" height="36" /></bpmndi:BPMNShape></bpmndi:BPMNPlane></bpmndi:BPMNDiagram></bpmn2:definitions>'
-
-      // model.value = {
-      //   key: 'flowable_01',
-      //   name: 'flowable测试',
-      //   description: 'ooxx',
-      //   category: '1',
-      //   formType: 10,
-      //   formId: 11,
-      //   formCustomCreatePath: null,
-      //   formCustomViewPath: null,
-      //   id: '4b4909d8-97e7-11ec-8e20-862bc1a4a054',
-      //   createTime: 1645978019795,
-      //   bpmnXml: undefined // 清空 bpmnXml 属性
-      // }
-      // console.log(modeler.value, 'modeler11111111')
-    })
-  }
-})
-const initModeler = (item) => {
-  setTimeout(() => {
-    modeler.value = item
-    console.log(item, 'initModeler方法modeler')
-    console.log(modeler.value, 'initModeler方法modeler')
-    // controlForm.value.prefix = '2222'
-  }, 10)
-}
-
-const save = (bpmnXml) => {
-  const data: ModelVO = {
-    ...(model.value ?? ({} as ModelVO)),
-    bpmnXml: bpmnXml // bpmnXml 只是初始化流程图,后续修改无法通过它获得
-  }
-  console.log(data, 'data')
-
-  // 修改的提交
-  if (data.id) {
-    updateModelApi(data).then((response) => {
-      console.log(response, 'response')
-      message.success('修改成功')
-      // 跳转回去
-      close()
-    })
-    return
-  }
-  // 添加的提交
-  createModelApi(data).then((response) => {
-    console.log(response, 'response1')
-    message.success('保存成功')
-    // 跳转回去
-    close()
-  })
-}
-/** 关闭按钮 */
-const close = () => {
-  router.push({ path: '/bpm/manager/model' })
-}
-</script>
-
-<style lang="scss">
-//body {
-//  overflow: hidden;
-//  margin: 0;
-//  box-sizing: border-box;
-//}
-//.app {
-//  width: 100%;
-//  height: 100%;
-//  box-sizing: border-box;
-//  display: inline-grid;
-//  grid-template-columns: 100px auto max-content;
-//}
-.demo-control-bar {
-  position: fixed;
-  right: 8px;
-  bottom: 8px;
-  z-index: 1;
-  .open-control-dialog {
-    width: 48px;
-    height: 48px;
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    border-radius: 4px;
-    font-size: 32px;
-    background: rgba(64, 158, 255, 1);
-    color: #ffffff;
-    cursor: pointer;
-  }
-}
-
-// TODO 芋艿:去掉多余的 faq
-//.info-tip {
-//  position: fixed;
-//  top: 40px;
-//  right: 500px;
-//  z-index: 10;
-//  color: #999999;
-//}
-
-.control-form {
-  .el-radio {
-    width: 100%;
-    line-height: 32px;
-  }
-}
-.element-overlays {
-  box-sizing: border-box;
-  padding: 8px;
-  background: rgba(0, 0, 0, 0.6);
-  border-radius: 4px;
-  color: #fafafa;
-}
-
-.my-process-designer {
-  height: calc(100vh - 84px);
-}
-.process-panel__container {
-  position: absolute;
-  right: 0;
-  top: 55px;
-  height: calc(100vh - 84px);
-}
-</style>

+ 0 - 154
src/views/bpm/processInstance/create.vue

@@ -1,154 +0,0 @@
-<template>
-  <ContentWrap>
-    <!-- 第一步,通过流程定义的列表,选择对应的流程 -->
-    <div v-if="!selectProcessInstance">
-      <XTable @register="registerTable">
-        <!-- 流程分类 -->
-        <template #category_default="{ row }">
-          <DictTag :type="DICT_TYPE.BPM_MODEL_CATEGORY" :value="Number(row?.category)" />
-        </template>
-        <template #version_default="{ row }">
-          <el-tag v-if="row">v{{ row.version }}</el-tag>
-        </template>
-        <template #actionbtns_default="{ row }">
-          <XTextButton preIcon="ep:plus" title="选择" @click="handleSelect(row)" />
-        </template>
-      </XTable>
-    </div>
-    <!-- 第二步,填写表单,进行流程的提交 -->
-    <div v-else>
-      <el-card class="box-card">
-        <div class="clearfix">
-          <span class="el-icon-document">申请信息【{{ selectProcessInstance.name }}】</span>
-          <XButton
-            style="float: right"
-            type="primary"
-            preIcon="ep:delete"
-            title="选择其它流程"
-            @click="selectProcessInstance = undefined"
-          />
-        </div>
-        <el-col :span="16" :offset="6" style="margin-top: 20px">
-          <form-create
-            :rule="detailForm.rule"
-            v-model:api="fApi"
-            :option="detailForm.option"
-            @submit="submitForm"
-          />
-        </el-col>
-      </el-card>
-      <el-card class="box-card">
-        <div class="clearfix">
-          <span class="el-icon-picture-outline">流程图</span>
-        </div>
-        <!-- TODO 芋艿:待完成??? -->
-        <my-process-viewer
-          key="designer"
-          v-model="bpmnXML"
-          :value="bpmnXML"
-          v-bind="bpmnControlForm"
-          :prefix="bpmnControlForm.prefix"
-        />
-      </el-card>
-    </div>
-  </ContentWrap>
-</template>
-<script setup lang="ts">
-// 业务相关的 import
-import { allSchemas } from './process.create'
-import * as DefinitionApi from '@/api/bpm/definition'
-import * as ProcessInstanceApi from '@/api/bpm/processInstance'
-import { setConfAndFields2 } from '@/utils/formCreate'
-import type { ApiAttrs } from '@form-create/element-ui/types/config'
-import { DICT_TYPE } from '@/utils/dict'
-
-const router = useRouter() // 路由
-const message = useMessage() // 消息
-
-// ========== 列表相关 ==========
-
-const [registerTable] = useXTable({
-  allSchemas: allSchemas,
-  params: {
-    suspensionState: 1
-  },
-  getListApi: DefinitionApi.getProcessDefinitionListApi,
-  isList: true
-})
-
-// ========== 表单相关 ==========
-
-const fApi = ref<ApiAttrs>()
-
-// 流程表单详情
-const detailForm = ref({
-  rule: [],
-  option: {}
-})
-
-// 流程表单
-const selectProcessInstance = ref() // 选择的流程实例
-/** 处理选择流程的按钮操作 **/
-const handleSelect = async (row) => {
-  // 设置选择的流程
-  selectProcessInstance.value = row
-
-  // 情况一:流程表单
-  if (row.formType == 10) {
-    // 设置表单
-    setConfAndFields2(detailForm, row.formConf, row.formFields)
-
-    // 加载流程图
-    DefinitionApi.getProcessDefinitionBpmnXMLApi(row.id).then((response) => {
-      bpmnXML.value = response
-    })
-    // 情况二:业务表单
-  } else if (row.formCustomCreatePath) {
-    await router.push({
-      path: row.formCustomCreatePath
-    })
-    // 这里暂时无需加载流程图,因为跳出到另外个 Tab;
-  }
-}
-
-/** 提交按钮 */
-const submitForm = async (formData) => {
-  if (!fApi.value || !selectProcessInstance.value) {
-    return
-  }
-
-  // 提交请求
-  fApi.value.btn.loading(true)
-  try {
-    await ProcessInstanceApi.createProcessInstanceApi({
-      processDefinitionId: selectProcessInstance.value.id,
-      variables: formData
-    })
-    // 提示
-    message.success('发起流程成功')
-    // this.$tab.closeOpenPage();
-    router.go(-1)
-  } finally {
-    fApi.value.btn.loading(false)
-  }
-}
-
-// ========== 流程图相关 ==========
-
-// // BPMN 数据
-const bpmnXML = ref(null)
-const bpmnControlForm = ref({
-  prefix: 'flowable'
-})
-</script>
-
-<style lang="scss">
-.my-process-designer {
-  height: calc(100vh - 200px);
-}
-
-.box-card {
-  width: 100%;
-  margin-bottom: 20px;
-}
-</style>

+ 130 - 0
src/views/bpm/processInstance/create/index.vue

@@ -0,0 +1,130 @@
+<template>
+  <!-- 第一步,通过流程定义的列表,选择对应的流程 -->
+  <ContentWrap v-if="!selectProcessInstance">
+    <el-table v-loading="loading" :data="list">
+      <el-table-column label="流程名称" align="center" prop="name" />
+      <el-table-column label="流程分类" align="center" prop="category">
+        <template #default="scope">
+          <dict-tag :type="DICT_TYPE.BPM_MODEL_CATEGORY" :value="scope.row.category" />
+        </template>
+      </el-table-column>
+      <el-table-column label="流程版本" align="center" prop="version">
+        <template #default="scope">
+          <el-tag>v{{ scope.row.version }}</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="流程描述" align="center" prop="description" />
+      <el-table-column label="操作" align="center">
+        <template #default="scope">
+          <el-button link type="primary" @click="handleSelect(scope.row)">
+            <Icon icon="ep:plus" /> 选择
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+  </ContentWrap>
+
+  <!-- 第二步,填写表单,进行流程的提交 -->
+  <ContentWrap v-else>
+    <el-card class="box-card">
+      <div class="clearfix">
+        <span class="el-icon-document">申请信息【{{ selectProcessInstance.name }}】</span>
+        <el-button style="float: right" type="primary" @click="selectProcessInstance = undefined">
+          <Icon icon="ep:delete" /> 选择其它流程
+        </el-button>
+      </div>
+      <el-col :span="16" :offset="6" style="margin-top: 20px">
+        <form-create
+          :rule="detailForm.rule"
+          v-model:api="fApi"
+          :option="detailForm.option"
+          @submit="submitForm"
+        />
+      </el-col>
+    </el-card>
+    <!-- 流程图预览 -->
+    <ProcessInstanceBpmnViewer :bpmn-xml="bpmnXML" />
+  </ContentWrap>
+</template>
+<script setup lang="ts">
+import { DICT_TYPE } from '@/utils/dict'
+import * as DefinitionApi from '@/api/bpm/definition'
+import * as ProcessInstanceApi from '@/api/bpm/processInstance'
+import { setConfAndFields2 } from '@/utils/formCreate'
+import type { ApiAttrs } from '@form-create/element-ui/types/config'
+import ProcessInstanceBpmnViewer from '../detail/ProcessInstanceBpmnViewer.vue'
+const router = useRouter() // 路由
+const message = useMessage() // 消息
+
+// ========== 列表相关 ==========
+const loading = ref(true) // 列表的加载中
+const list = ref([]) // 列表的数据
+const queryParams = reactive({
+  suspensionState: 1
+})
+
+/** 查询列表 */
+const getList = async () => {
+  loading.value = true
+  try {
+    list.value = await DefinitionApi.getProcessDefinitionList(queryParams)
+  } finally {
+    loading.value = false
+  }
+}
+
+// ========== 表单相关 ==========
+const bpmnXML = ref(null) // BPMN 数据
+const fApi = ref<ApiAttrs>()
+const detailForm = ref({
+  // 流程表单详情
+  rule: [],
+  option: {}
+})
+const selectProcessInstance = ref() // 选择的流程实例
+
+/** 处理选择流程的按钮操作 **/
+const handleSelect = async (row) => {
+  // 设置选择的流程
+  selectProcessInstance.value = row
+
+  // 情况一:流程表单
+  if (row.formType == 10) {
+    // 设置表单
+    setConfAndFields2(detailForm, row.formConf, row.formFields)
+    // 加载流程图
+    bpmnXML.value = await DefinitionApi.getProcessDefinitionBpmnXML(row.id)
+    // 情况二:业务表单
+  } else if (row.formCustomCreatePath) {
+    await router.push({
+      path: row.formCustomCreatePath
+    })
+    // 这里暂时无需加载流程图,因为跳出到另外个 Tab;
+  }
+}
+
+/** 提交按钮 */
+const submitForm = async (formData) => {
+  if (!fApi.value || !selectProcessInstance.value) {
+    return
+  }
+  // 提交请求
+  fApi.value.btn.loading(true)
+  try {
+    await ProcessInstanceApi.createProcessInstanceApi({
+      processDefinitionId: selectProcessInstance.value.id,
+      variables: formData
+    })
+    // 提示
+    message.success('发起流程成功')
+    router.go(-1)
+  } finally {
+    fApi.value.btn.loading(false)
+  }
+}
+
+/** 初始化 */
+onMounted(() => {
+  getList()
+})
+</script>

+ 0 - 490
src/views/bpm/processInstance/detail.vue

@@ -1,490 +0,0 @@
-<template>
-  <ContentWrap>
-    <!-- 审批信息 -->
-    <el-card
-      class="box-card"
-      v-loading="processInstanceLoading"
-      v-for="(item, index) in runningTasks"
-      :key="index"
-    >
-      <template #header>
-        <span class="el-icon-picture-outline">审批任务【{{ item.name }}】</span>
-      </template>
-      <el-col :span="16" :offset="6">
-        <el-form
-          :ref="'form' + index"
-          :model="auditForms[index]"
-          :rules="auditRule"
-          label-width="100px"
-        >
-          <el-form-item label="流程名" v-if="processInstance && processInstance.name">
-            {{ processInstance.name }}
-          </el-form-item>
-          <el-form-item label="流程发起人" v-if="processInstance && processInstance.startUser">
-            {{ processInstance.startUser.nickname }}
-            <el-tag type="info" size="small">{{ processInstance.startUser.deptName }}</el-tag>
-          </el-form-item>
-          <el-form-item label="审批建议" prop="reason">
-            <el-input
-              type="textarea"
-              v-model="auditForms[index].reason"
-              placeholder="请输入审批建议"
-            />
-          </el-form-item>
-        </el-form>
-        <div style="margin-left: 10%; margin-bottom: 20px; font-size: 14px">
-          <XButton
-            pre-icon="ep:select"
-            type="success"
-            title="通过"
-            @click="handleAudit(item, true)"
-          />
-          <XButton
-            pre-icon="ep:close"
-            type="danger"
-            title="不通过"
-            @click="handleAudit(item, false)"
-          />
-          <XButton
-            pre-icon="ep:edit"
-            type="primary"
-            title="转办"
-            @click="handleUpdateAssignee(item)"
-          />
-          <XButton
-            pre-icon="ep:position"
-            type="primary"
-            title="委派"
-            @click="handleDelegate(item)"
-          />
-          <XButton pre-icon="ep:back" type="warning" title="委派" @click="handleBack(item)" />
-        </div>
-      </el-col>
-    </el-card>
-
-    <!-- 申请信息 -->
-    <el-card class="box-card" v-loading="processInstanceLoading">
-      <template #header>
-        <span class="el-icon-document">申请信息【{{ processInstance.name }}】</span>
-      </template>
-      <!-- 情况一:流程表单 -->
-      <el-col v-if="processInstance?.processDefinition?.formType === 10" :span="16" :offset="6">
-        <form-create
-          ref="fApi"
-          :rule="detailForm.rule"
-          :option="detailForm.option"
-          v-model="detailForm.value"
-        />
-      </el-col>
-      <!-- 情况二:流程表单 -->
-      <div v-if="processInstance?.processDefinition?.formType === 20">
-        <router-link
-          :to="
-            processInstance.processDefinition.formCustomViewPath +
-            '?id=' +
-            processInstance.businessKey
-          "
-        >
-          <XButton type="primary" preIcon="ep:view" title="点击查看" />
-        </router-link>
-      </div>
-    </el-card>
-
-    <!-- 审批记录 -->
-    <el-card class="box-card" v-loading="tasksLoad">
-      <template #header>
-        <span class="el-icon-picture-outline">审批记录</span>
-      </template>
-      <el-col :span="16" :offset="4">
-        <div class="block">
-          <el-timeline>
-            <el-timeline-item
-              v-for="(item, index) in tasks"
-              :key="index"
-              :icon="getTimelineItemIcon(item)"
-              :type="getTimelineItemType(item)"
-            >
-              <p style="font-weight: 700">任务:{{ item.name }}</p>
-              <el-card :body-style="{ padding: '10px' }">
-                <label v-if="item.assigneeUser" style="font-weight: normal; margin-right: 30px">
-                  审批人:{{ item.assigneeUser.nickname }}
-                  <el-tag type="info" size="small">{{ item.assigneeUser.deptName }}</el-tag>
-                </label>
-                <label style="font-weight: normal" v-if="item.createTime">创建时间:</label>
-                <label style="color: #8a909c; font-weight: normal">
-                  {{ dayjs(item?.createTime).format('YYYY-MM-DD HH:mm:ss') }}
-                </label>
-                <label v-if="item.endTime" style="margin-left: 30px; font-weight: normal">
-                  审批时间:
-                </label>
-                <label v-if="item.endTime" style="color: #8a909c; font-weight: normal">
-                  {{ dayjs(item?.endTime).format('YYYY-MM-DD HH:mm:ss') }}
-                </label>
-                <label v-if="item.durationInMillis" style="margin-left: 30px; font-weight: normal">
-                  耗时:
-                </label>
-                <label v-if="item.durationInMillis" style="color: #8a909c; font-weight: normal">
-                  {{ formatPast2(item?.durationInMillis) }}
-                </label>
-                <p v-if="item.reason">
-                  <el-tag :type="getTimelineItemType(item)">{{ item.reason }}</el-tag>
-                </p>
-              </el-card>
-            </el-timeline-item>
-          </el-timeline>
-        </div>
-      </el-col>
-    </el-card>
-
-    <!-- 高亮流程图 -->
-    <el-card class="box-card" v-loading="processInstanceLoading">
-      <template #header>
-        <span class="el-icon-picture-outline">流程图</span>
-      </template>
-      <my-process-viewer
-        key="designer"
-        v-model="bpmnXML"
-        :value="bpmnXML"
-        v-bind="bpmnControlForm"
-        :prefix="bpmnControlForm.prefix"
-        :activityData="activityList"
-        :processInstanceData="processInstance"
-        :taskData="tasks"
-      />
-    </el-card>
-
-    <!-- 对话框(转派审批人) -->
-    <XModal v-model="updateAssigneeVisible" title="转派审批人" width="500">
-      <el-form
-        ref="updateAssigneeFormRef"
-        :model="updateAssigneeForm"
-        :rules="updateAssigneeRules"
-        label-width="110px"
-      >
-        <el-form-item label="新审批人" prop="assigneeUserId">
-          <el-select v-model="updateAssigneeForm.assigneeUserId" clearable style="width: 100%">
-            <el-option
-              v-for="item in userOptions"
-              :key="parseInt(item.id)"
-              :label="item.nickname"
-              :value="parseInt(item.id)"
-            />
-          </el-select>
-        </el-form-item>
-      </el-form>
-      <!-- 操作按钮 -->
-      <template #footer>
-        <!-- 按钮:保存 -->
-        <XButton
-          type="primary"
-          :title="t('action.save')"
-          :loading="updateAssigneeLoading"
-          @click="submitUpdateAssigneeForm"
-        />
-        <!-- 按钮:关闭 -->
-        <XButton
-          :loading="updateAssigneeLoading"
-          :title="t('dialog.close')"
-          @click="updateAssigneeLoading = false"
-        />
-      </template>
-    </XModal>
-  </ContentWrap>
-</template>
-<script setup lang="ts">
-import dayjs from 'dayjs'
-import * as UserApi from '@/api/system/user'
-import * as ProcessInstanceApi from '@/api/bpm/processInstance'
-import * as DefinitionApi from '@/api/bpm/definition'
-import * as TaskApi from '@/api/bpm/task'
-import * as ActivityApi from '@/api/bpm/activity'
-import { formatPast2 } from '@/utils/formatTime'
-import { setConfAndFields2 } from '@/utils/formCreate'
-// import { OptionAttrs } from '@form-create/element-ui/types/config'
-import type { ApiAttrs } from '@form-create/element-ui/types/config'
-import { useUserStore } from '@/store/modules/user'
-
-const { query } = useRoute() // 查询参数
-const message = useMessage() // 消息弹窗
-const { t } = useI18n() // 国际化
-const { proxy } = getCurrentInstance() as any
-
-// ========== 审批信息 ==========
-const id = query.id as unknown as number
-const processInstanceLoading = ref(false) // 流程实例的加载中
-const processInstance = ref<any>({}) // 流程实例
-const runningTasks = ref<any[]>([]) // 运行中的任务
-const auditForms = ref<any[]>([]) // 审批任务的表单
-const auditRule = reactive({
-  reason: [{ required: true, message: '审批建议不能为空', trigger: 'blur' }]
-})
-
-// 处理审批通过和不通过的操作
-const handleAudit = async (task, pass) => {
-  // 1.1 获得对应表单
-  const index = runningTasks.value.indexOf(task)
-  const auditFormRef = proxy.$refs['form' + index][0]
-  // alert(auditFormRef)
-
-  // 1.2 校验表单
-  const elForm = unref(auditFormRef)
-  if (!elForm) return
-  const valid = await elForm.validate()
-  if (!valid) return
-
-  // 2.1 提交审批
-  const data = {
-    id: task.id,
-    reason: auditForms.value[index].reason
-  }
-  if (pass) {
-    await TaskApi.approveTask(data)
-    message.success('审批通过成功')
-  } else {
-    await TaskApi.rejectTask(data)
-    message.success('审批不通过成功')
-  }
-  // 2.2 加载最新数据
-  getDetail()
-}
-
-// ========== 申请信息 ==========
-const fApi = ref<ApiAttrs>()
-const userId = useUserStore().getUser.id // 当前登录的编号
-// 流程表单详情
-const detailForm = ref({
-  rule: [],
-  option: {},
-  value: {}
-})
-
-// ========== 审批记录 ==========
-const tasksLoad = ref(true)
-const tasks = ref<any[]>([])
-
-const getTimelineItemIcon = (item) => {
-  if (item.result === 1) {
-    return 'el-icon-time'
-  }
-  if (item.result === 2) {
-    return 'el-icon-check'
-  }
-  if (item.result === 3) {
-    return 'el-icon-close'
-  }
-  if (item.result === 4) {
-    return 'el-icon-remove-outline'
-  }
-  return ''
-}
-const getTimelineItemType = (item) => {
-  if (item.result === 1) {
-    return 'primary'
-  }
-  if (item.result === 2) {
-    return 'success'
-  }
-  if (item.result === 3) {
-    return 'danger'
-  }
-  if (item.result === 4) {
-    return 'info'
-  }
-  return ''
-}
-
-// ========== 审批记录 ==========
-const updateAssigneeVisible = ref(false)
-const updateAssigneeLoading = ref(false)
-const updateAssigneeForm = ref({
-  id: undefined,
-  assigneeUserId: undefined
-})
-const updateAssigneeRules = ref({
-  assigneeUserId: [{ required: true, message: '新审批人不能为空', trigger: 'change' }]
-})
-const updateAssigneeFormRef = ref()
-const userOptions = ref<any[]>([])
-
-// 处理转派审批人
-const handleUpdateAssignee = (task) => {
-  // 设置表单
-  resetUpdateAssigneeForm()
-  updateAssigneeForm.value.id = task.id
-  // 设置为打开
-  updateAssigneeVisible.value = true
-}
-
-// 提交转派审批人
-const submitUpdateAssigneeForm = async () => {
-  // 1. 校验表单
-  const elForm = unref(updateAssigneeFormRef)
-  if (!elForm) return
-  const valid = await elForm.validate()
-  if (!valid) return
-
-  // 2.1 提交审批
-  updateAssigneeLoading.value = true
-  try {
-    await TaskApi.updateTaskAssignee(updateAssigneeForm.value)
-    // 2.2 设置为隐藏
-    updateAssigneeVisible.value = false
-    // 加载最新数据
-    getDetail()
-  } finally {
-    updateAssigneeLoading.value = false
-  }
-}
-
-// 重置转派审批人表单
-const resetUpdateAssigneeForm = () => {
-  updateAssigneeForm.value = {
-    id: undefined,
-    assigneeUserId: undefined
-  }
-  updateAssigneeFormRef.value?.resetFields()
-}
-
-/** 处理审批退回的操作 */
-const handleDelegate = async (task) => {
-  message.error('暂不支持【委派】功能,可以使用【转派】替代!')
-  console.log(task)
-}
-
-/** 处理审批退回的操作 */
-const handleBack = async (task) => {
-  message.error('暂不支持【退回】功能!')
-  // 可参考 http://blog.wya1.com/article/636697030/details/7296
-  // const data = {
-  //   id: task.id,
-  //   assigneeUserId: 1
-  // }
-  // backTask(data).then(response => {
-  //   this.$modal.msgSuccess("回退成功!");
-  //   this.getDetail(); // 获得最新详情
-  // });
-  console.log(task)
-}
-
-// ========== 高亮流程图 ==========
-const bpmnXML = ref(null)
-const bpmnControlForm = ref({
-  prefix: 'flowable'
-})
-const activityList = ref([])
-
-// ========== 初始化 ==========
-onMounted(() => {
-  // 加载详情
-  getDetail()
-  // 加载用户的列表
-  UserApi.getListSimpleUsersApi().then((data) => {
-    userOptions.value.push(...data)
-  })
-})
-
-const getDetail = () => {
-  // 1. 获得流程实例相关
-  processInstanceLoading.value = true
-  ProcessInstanceApi.getProcessInstanceApi(id)
-    .then((data) => {
-      if (!data) {
-        message.error('查询不到流程信息!')
-        return
-      }
-      processInstance.value = data
-
-      // 设置表单信息
-      const processDefinition = data.processDefinition
-      if (processDefinition.formType === 10) {
-        setConfAndFields2(
-          detailForm,
-          processDefinition.formConf,
-          processDefinition.formFields,
-          data.formVariables
-        )
-        nextTick().then(() => {
-          fApi.value?.fapi?.btn.show(false)
-          fApi.value?.fapi?.resetBtn.show(false)
-          fApi.value?.fapi?.disabled(true)
-        })
-      }
-
-      // 加载流程图
-      DefinitionApi.getProcessDefinitionBpmnXMLApi(processDefinition.id).then((data) => {
-        bpmnXML.value = data
-      })
-
-      // 加载活动列表
-      ActivityApi.getActivityList({
-        processInstanceId: data.id
-      }).then((data) => {
-        activityList.value = data
-      })
-    })
-    .finally(() => {
-      processInstanceLoading.value = false
-    })
-
-  // 2. 获得流程任务列表(审批记录)
-  tasksLoad.value = true
-  runningTasks.value = []
-  auditForms.value = []
-  TaskApi.getTaskListByProcessInstanceId(id)
-    .then((data) => {
-      // 审批记录
-      tasks.value = []
-      // 移除已取消的审批
-      data.forEach((task) => {
-        if (task.result !== 4) {
-          tasks.value.push(task)
-        }
-      })
-      // 排序,将未完成的排在前面,已完成的排在后面;
-      tasks.value.sort((a, b) => {
-        // 有已完成的情况,按照完成时间倒序
-        if (a.endTime && b.endTime) {
-          return b.endTime - a.endTime
-        } else if (a.endTime) {
-          return 1
-        } else if (b.endTime) {
-          return -1
-          // 都是未完成,按照创建时间倒序
-        } else {
-          return b.createTime - a.createTime
-        }
-      })
-
-      // 需要审核的记录
-      tasks.value.forEach((task) => {
-        // 1.1 只有待处理才需要
-        if (task.result !== 1) {
-          return
-        }
-        // 1.2 自己不是处理人
-        if (!task.assigneeUser || task.assigneeUser.id !== userId) {
-          return
-        }
-        // 2. 添加到处理任务
-        runningTasks.value.push({ ...task })
-        auditForms.value.push({
-          reason: ''
-        })
-      })
-    })
-    .finally(() => {
-      tasksLoad.value = false
-    })
-}
-</script>
-
-<style lang="scss">
-.my-process-designer {
-  height: calc(100vh - 200px);
-}
-
-.box-card {
-  width: 100%;
-  margin-bottom: 20px;
-}
-</style>

+ 55 - 0
src/views/bpm/processInstance/detail/ProcessInstanceBpmnViewer.vue

@@ -0,0 +1,55 @@
+<template>
+  <el-card class="box-card" v-loading="loading">
+    <template #header>
+      <span class="el-icon-picture-outline">流程图</span>
+    </template>
+    <my-process-viewer
+      key="designer"
+      :value="bpmnXml"
+      v-bind="bpmnControlForm"
+      :prefix="bpmnControlForm.prefix"
+      :activityData="activityList"
+      :processInstanceData="processInstance"
+      :taskData="tasks"
+    />
+  </el-card>
+</template>
+<script setup lang="ts">
+import { propTypes } from '@/utils/propTypes'
+import * as ActivityApi from '@/api/bpm/activity'
+// import * as DefinitionApi from '@/api/bpm/definition'
+
+const props = defineProps({
+  loading: propTypes.bool, // 是否加载中
+  id: propTypes.string, // 流程实例的编号
+  processInstance: propTypes.any, // 流程实例的信息
+  tasks: propTypes.array, // 流程任务的数组
+  bpmnXml: propTypes.string // BPMN XML
+})
+
+const bpmnControlForm = ref({
+  prefix: 'flowable'
+})
+const activityList = ref([]) // 任务列表
+// const bpmnXML = computed(() => { // TODO 芋艿:不晓得为啊哈不能这么搞
+//   if (!props.processInstance || !props.processInstance.processDefinition) {
+//     return
+//   }
+//   return DefinitionApi.getProcessDefinitionBpmnXML(props.processInstance.processDefinition.id)
+// })
+
+/** 初始化 */
+onMounted(async () => {
+  if (props.id) {
+    activityList.value = await ActivityApi.getActivityList({
+      processInstanceId: props.id
+    })
+  }
+})
+</script>
+<style>
+.box-card {
+  width: 100%;
+  margin-bottom: 20px;
+}
+</style>

+ 89 - 0
src/views/bpm/processInstance/detail/ProcessInstanceTaskList.vue

@@ -0,0 +1,89 @@
+<template>
+  <el-card class="box-card" v-loading="loading">
+    <template #header>
+      <span class="el-icon-picture-outline">审批记录</span>
+    </template>
+    <el-col :span="16" :offset="4">
+      <div class="block">
+        <el-timeline>
+          <el-timeline-item
+            v-for="(item, index) in tasks"
+            :key="index"
+            :icon="getTimelineItemIcon(item)"
+            :type="getTimelineItemType(item)"
+          >
+            <p style="font-weight: 700">任务:{{ item.name }}</p>
+            <el-card :body-style="{ padding: '10px' }">
+              <label v-if="item.assigneeUser" style="font-weight: normal; margin-right: 30px">
+                审批人:{{ item.assigneeUser.nickname }}
+                <el-tag type="info" size="small">{{ item.assigneeUser.deptName }}</el-tag>
+              </label>
+              <label style="font-weight: normal" v-if="item.createTime">创建时间:</label>
+              <label style="color: #8a909c; font-weight: normal">
+                {{ parseTime(item?.createTime) }}
+              </label>
+              <label v-if="item.endTime" style="margin-left: 30px; font-weight: normal">
+                审批时间:
+              </label>
+              <label v-if="item.endTime" style="color: #8a909c; font-weight: normal">
+                {{ parseTime(item?.endTime) }}
+              </label>
+              <label v-if="item.durationInMillis" style="margin-left: 30px; font-weight: normal">
+                耗时:
+              </label>
+              <label v-if="item.durationInMillis" style="color: #8a909c; font-weight: normal">
+                {{ formatPast2(item?.durationInMillis) }}
+              </label>
+              <p v-if="item.reason">
+                <el-tag :type="getTimelineItemType(item)">{{ item.reason }}</el-tag>
+              </p>
+            </el-card>
+          </el-timeline-item>
+        </el-timeline>
+      </div>
+    </el-col>
+  </el-card>
+</template>
+<script setup lang="ts">
+import { parseTime, formatPast2 } from '@/utils/formatTime'
+import { propTypes } from '@/utils/propTypes'
+
+defineProps({
+  loading: propTypes.bool, // 是否加载中
+  tasks: propTypes.array // 流程任务的数组
+})
+
+/** 获得任务对应的 icon */
+const getTimelineItemIcon = (item) => {
+  if (item.result === 1) {
+    return 'el-icon-time'
+  }
+  if (item.result === 2) {
+    return 'el-icon-check'
+  }
+  if (item.result === 3) {
+    return 'el-icon-close'
+  }
+  if (item.result === 4) {
+    return 'el-icon-remove-outline'
+  }
+  return ''
+}
+
+/** 获得任务对应的颜色 */
+const getTimelineItemType = (item) => {
+  if (item.result === 1) {
+    return 'primary'
+  }
+  if (item.result === 2) {
+    return 'success'
+  }
+  if (item.result === 3) {
+    return 'danger'
+  }
+  if (item.result === 4) {
+    return 'info'
+  }
+  return ''
+}
+</script>

+ 81 - 0
src/views/bpm/processInstance/detail/TaskUpdateAssigneeForm.vue

@@ -0,0 +1,81 @@
+<template>
+  <Dialog title="转派审批人" v-model="modelVisible" width="500">
+    <el-form
+      ref="formRef"
+      :model="formData"
+      :rules="formRules"
+      label-width="110px"
+      v-loading="formLoading"
+    >
+      <el-form-item label="新审批人" prop="assigneeUserId">
+        <el-select v-model="formData.assigneeUserId" clearable style="width: 100%">
+          <el-option
+            v-for="item in userList"
+            :key="item.id"
+            :label="item.nickname"
+            :value="item.id"
+          />
+        </el-select>
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
+      <el-button @click="modelVisible = false">取 消</el-button>
+    </template>
+  </Dialog>
+</template>
+<script setup lang="ts">
+import * as TaskApi from '@/api/bpm/task'
+import * as UserApi from '@/api/system/user'
+
+const modelVisible = ref(false) // 弹窗的是否展示
+const formLoading = ref(false) // 表单的加载中
+const formData = ref({
+  id: '',
+  assigneeUserId: undefined
+})
+const formRules = ref({
+  assigneeUserId: [{ required: true, message: '新审批人不能为空', trigger: 'change' }]
+})
+
+const formRef = ref() // 表单 Ref
+const userList = ref<any[]>([]) // 用户列表
+
+/** 打开弹窗 */
+const open = async (id: string) => {
+  modelVisible.value = true
+  resetForm()
+  formData.value.id = id
+  // 获得用户列表
+  userList.value = await UserApi.getSimpleUserList()
+}
+defineExpose({ open }) // 提供 openModal 方法,用于打开弹窗
+
+/** 提交表单 */
+const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
+const submitForm = async () => {
+  // 校验表单
+  if (!formRef) return
+  const valid = await formRef.value.validate()
+  if (!valid) return
+  // 提交请求
+  formLoading.value = true
+  try {
+    await TaskApi.updateTaskAssignee(formData.value)
+    modelVisible.value = false
+    // 发送操作成功的事件
+    emit('success')
+  } finally {
+    formLoading.value = false
+  }
+}
+
+/** 重置表单 */
+const resetForm = () => {
+  formData.value = {
+    id: '',
+    assigneeUserId: undefined
+  }
+  formRef.value?.resetFields()
+}
+</script>

+ 277 - 0
src/views/bpm/processInstance/detail/index.vue

@@ -0,0 +1,277 @@
+<template>
+  <ContentWrap>
+    <!-- 审批信息 -->
+    <el-card
+      class="box-card"
+      v-loading="processInstanceLoading"
+      v-for="(item, index) in runningTasks"
+      :key="index"
+    >
+      <template #header>
+        <span class="el-icon-picture-outline">审批任务【{{ item.name }}】</span>
+      </template>
+      <el-col :span="16" :offset="6">
+        <el-form
+          :ref="'form' + index"
+          :model="auditForms[index]"
+          :rules="auditRule"
+          label-width="100px"
+        >
+          <el-form-item label="流程名" v-if="processInstance && processInstance.name">
+            {{ processInstance.name }}
+          </el-form-item>
+          <el-form-item label="流程发起人" v-if="processInstance && processInstance.startUser">
+            {{ processInstance.startUser.nickname }}
+            <el-tag type="info" size="small">{{ processInstance.startUser.deptName }}</el-tag>
+          </el-form-item>
+          <el-form-item label="审批建议" prop="reason">
+            <el-input
+              type="textarea"
+              v-model="auditForms[index].reason"
+              placeholder="请输入审批建议"
+            />
+          </el-form-item>
+        </el-form>
+        <div style="margin-left: 10%; margin-bottom: 20px; font-size: 14px">
+          <el-button type="success" @click="handleAudit(item, true)">
+            <Icon icon="ep:select" /> 通过
+          </el-button>
+          <el-button type="danger" @click="handleAudit(item, false)">
+            <Icon icon="ep:close" /> 不通过
+          </el-button>
+          <el-button type="primary" @click="openTaskUpdateAssigneeForm(item.id)">
+            <Icon icon="ep:edit" /> 转办
+          </el-button>
+          <el-button type="primary" @click="handleDelegate(item)">
+            <Icon icon="ep:position" /> 委派
+          </el-button>
+          <el-button type="warning" @click="handleBack(item)">
+            <Icon icon="ep:back" /> 回退
+          </el-button>
+        </div>
+      </el-col>
+    </el-card>
+
+    <!-- 申请信息 -->
+    <el-card class="box-card" v-loading="processInstanceLoading">
+      <template #header>
+        <span class="el-icon-document">申请信息【{{ processInstance.name }}】</span>
+      </template>
+      <!-- 情况一:流程表单 -->
+      <el-col v-if="processInstance?.processDefinition?.formType === 10" :span="16" :offset="6">
+        <form-create
+          ref="fApi"
+          :rule="detailForm.rule"
+          :option="detailForm.option"
+          v-model="detailForm.value"
+        />
+      </el-col>
+      <!-- 情况二:流程表单 -->
+      <div v-if="processInstance?.processDefinition?.formType === 20">
+        <router-link
+          :to="
+            processInstance.processDefinition.formCustomViewPath +
+            '?id=' +
+            processInstance.businessKey
+          "
+        >
+          <el-button type="primary"><Icon icon="ep:view" /> 点击查看</el-button>
+        </router-link>
+      </div>
+    </el-card>
+
+    <!-- 审批记录 -->
+    <ProcessInstanceTaskList :loading="tasksLoad" :tasks="tasks" />
+
+    <!-- 高亮流程图 -->
+    <ProcessInstanceBpmnViewer
+      :id="id"
+      :process-instance="processInstance"
+      :loading="processInstanceLoading"
+      :tasks="tasks"
+      :bpmn-xml="bpmnXML"
+    />
+
+    <!-- 弹窗:转派审批人 -->
+    <TaskUpdateAssigneeForm ref="taskUpdateAssigneeFormRef" @success="getDetail" />
+  </ContentWrap>
+</template>
+<script setup lang="ts">
+import { useUserStore } from '@/store/modules/user'
+import { setConfAndFields2 } from '@/utils/formCreate'
+import type { ApiAttrs } from '@form-create/element-ui/types/config'
+import * as DefinitionApi from '@/api/bpm/definition'
+import * as ProcessInstanceApi from '@/api/bpm/processInstance'
+import * as TaskApi from '@/api/bpm/task'
+import TaskUpdateAssigneeForm from './TaskUpdateAssigneeForm.vue'
+import ProcessInstanceBpmnViewer from './ProcessInstanceBpmnViewer.vue'
+import ProcessInstanceTaskList from './ProcessInstanceTaskList.vue'
+const { query } = useRoute() // 查询参数
+const message = useMessage() // 消息弹窗
+const { proxy } = getCurrentInstance() as any
+
+const userId = useUserStore().getUser.id // 当前登录的编号
+const id = query.id as unknown as number // 流程实例的编号
+const processInstanceLoading = ref(false) // 流程实例的加载中
+const processInstance = ref<any>({}) // 流程实例
+const bpmnXML = ref('') // BPMN XML
+const tasksLoad = ref(true) // 任务的加载中
+const tasks = ref<any[]>([]) // 任务列表
+// ========== 审批信息 ==========
+const runningTasks = ref<any[]>([]) // 运行中的任务
+const auditForms = ref<any[]>([]) // 审批任务的表单
+const auditRule = reactive({
+  reason: [{ required: true, message: '审批建议不能为空', trigger: 'blur' }]
+})
+// ========== 申请信息 ==========
+const fApi = ref<ApiAttrs>() //
+const detailForm = ref({
+  // 流程表单详情
+  rule: [],
+  option: {},
+  value: {}
+})
+
+/** 处理审批通过和不通过的操作 */
+const handleAudit = async (task, pass) => {
+  // 1.1 获得对应表单
+  const index = runningTasks.value.indexOf(task)
+  const auditFormRef = proxy.$refs['form' + index][0]
+  // 1.2 校验表单
+  const elForm = unref(auditFormRef)
+  if (!elForm) return
+  const valid = await elForm.validate()
+  if (!valid) return
+
+  // 2.1 提交审批
+  const data = {
+    id: task.id,
+    reason: auditForms.value[index].reason
+  }
+  if (pass) {
+    await TaskApi.approveTask(data)
+    message.success('审批通过成功')
+  } else {
+    await TaskApi.rejectTask(data)
+    message.success('审批不通过成功')
+  }
+  // 2.2 加载最新数据
+  getDetail()
+}
+
+/** 转派审批人 */
+const taskUpdateAssigneeFormRef = ref()
+const openTaskUpdateAssigneeForm = (id: string) => {
+  taskUpdateAssigneeFormRef.value.open(id)
+}
+
+/** 处理审批退回的操作 */
+const handleDelegate = async (task) => {
+  message.error('暂不支持【委派】功能,可以使用【转派】替代!')
+  console.log(task)
+}
+
+/** 处理审批退回的操作 */
+const handleBack = async (task) => {
+  message.error('暂不支持【退回】功能!')
+  console.log(task)
+}
+
+/** 获得详情 */
+const getDetail = () => {
+  // 1. 获得流程实例相关
+  getProcessInstance()
+  // 2. 获得流程任务列表(审批记录)
+  getTaskList()
+}
+
+/** 加载流程实例 */
+const getProcessInstance = async () => {
+  try {
+    processInstanceLoading.value = true
+    const data = await ProcessInstanceApi.getProcessInstanceApi(id)
+    if (!data) {
+      message.error('查询不到流程信息!')
+      return
+    }
+    processInstance.value = data
+
+    // 设置表单信息
+    const processDefinition = data.processDefinition
+    if (processDefinition.formType === 10) {
+      setConfAndFields2(
+        detailForm,
+        processDefinition.formConf,
+        processDefinition.formFields,
+        data.formVariables
+      )
+      nextTick().then(() => {
+        fApi.value?.fapi?.btn.show(false)
+        fApi.value?.fapi?.resetBtn.show(false)
+        fApi.value?.fapi?.disabled(true)
+      })
+    }
+
+    // 加载流程图
+    bpmnXML.value = await DefinitionApi.getProcessDefinitionBpmnXML(processDefinition.id)
+  } finally {
+    processInstanceLoading.value = false
+  }
+}
+
+/** 加载任务列表 */
+const getTaskList = async () => {
+  try {
+    // 获得未取消的任务
+    tasksLoad.value = true
+    const data = await TaskApi.getTaskListByProcessInstanceId(id)
+    tasks.value = []
+    // 1.1 移除已取消的审批
+    data.forEach((task) => {
+      if (task.result !== 4) {
+        tasks.value.push(task)
+      }
+    })
+    // 1.2 排序,将未完成的排在前面,已完成的排在后面;
+    tasks.value.sort((a, b) => {
+      // 有已完成的情况,按照完成时间倒序
+      if (a.endTime && b.endTime) {
+        return b.endTime - a.endTime
+      } else if (a.endTime) {
+        return 1
+      } else if (b.endTime) {
+        return -1
+        // 都是未完成,按照创建时间倒序
+      } else {
+        return b.createTime - a.createTime
+      }
+    })
+
+    // 获得需要自己审批的任务
+    runningTasks.value = []
+    auditForms.value = []
+    tasks.value.forEach((task) => {
+      // 2.1 只有待处理才需要
+      if (task.result !== 1) {
+        return
+      }
+      // 2.2 自己不是处理人
+      if (!task.assigneeUser || task.assigneeUser.id !== userId) {
+        return
+      }
+      // 2.3 添加到处理任务
+      runningTasks.value.push({ ...task })
+      auditForms.value.push({
+        reason: ''
+      })
+    })
+  } finally {
+    tasksLoad.value = false
+  }
+}
+
+/** 初始化 */
+onMounted(() => {
+  getDetail()
+})
+</script>

部分文件因文件數量過多而無法顯示