From 6350384ee189b076372b6570331a50abbc2a053c Mon Sep 17 00:00:00 2001 From: du <13220750630.163.com> Date: 星期四, 26 六月 2025 16:32:43 +0800 Subject: [PATCH] 流程定义 --- ruoyi-ui/apps/web-antd/src/package/designer/plugins/translate/customTranslate.js | 41 ruoyi-ui/apps/web-antd/src/package/theme/element-variables.scss | 70 ruoyi-ui/apps/web-antd/src/package/penal/listeners/ElementListeners.vue | 302 + ruoyi-ui/apps/web-antd/src/package/designer/plugins/content-pad/contentPadProvider.js | 390 + ruoyi-ui/apps/web-antd/src/views/workflow/work/claim.vue | 129 ruoyi-ui/apps/web-antd/src/package/designer/plugins/descriptor/flowableDescriptor.json | 1230 +++++ ruoyi-ui/apps/web-antd/src/package/penal/task/task-components/ScriptTask.vue | 85 ruoyi-ui/apps/web-antd/src/package/penal/task/ElementTask.vue | 72 ruoyi-ui/apps/web-antd/src/views/workflow/work/todo.vue | 132 ruoyi-ui/apps/web-antd/src/api/workflow/listener/index.ts | 45 ruoyi-ui/apps/web-antd/src/package/Log.js | 99 ruoyi-ui/apps/web-antd/src/package/designer/plugins/palette/CustomPalette.js | 156 ruoyi-ui/apps/web-antd/src/views/workflow/work/start.vue | 75 ruoyi-ui/apps/web-antd/src/modules/auto-place/index.js | 6 ruoyi-ui/apps/web-antd/src/package/penal/listeners/UserTaskListeners.vue | 335 + ruoyi-ui/apps/web-antd/src/views/workflow/form/index.vue | 251 + ruoyi-ui/apps/web-antd/src/api/workflow/category/index1.ts | 63 ruoyi-ui/apps/web-antd/src/views/workflow/category/index.vue | 335 ruoyi-ui/apps/web-antd/src/package/penal/multi-instance/utilSelf.js | 80 ruoyi-ui/apps/web-antd/src/package/penal/multi-instance/ElementMultiInstance.vue | 331 + ruoyi-ui/apps/web-antd/src/package/penal/listeners/template.js | 178 ruoyi-ui/apps/web-antd/src/package/penal/properties/ElementProperties.vue | 141 ruoyi-ui/apps/web-antd/src/api/workflow/form/index.ts | 24 ruoyi-ui/apps/web-antd/src/api/workflow/model/index.ts | 47 ruoyi-ui/apps/web-antd/src/package/designer/plugins/palette/paletteProvider.js | 160 ruoyi-ui/apps/web-antd/src/package/penal/task/task-components/ReceiveTask.vue | 102 ruoyi-ui/apps/web-antd/src/api/workflow/work/task.ts | 35 ruoyi-ui/apps/web-antd/src/views/workflow/work/own.vue | 193 ruoyi-ui/apps/web-antd/src/api/workflow/identity/index.ts | 11 ruoyi-ui/apps/web-antd/src/modules/auto-place/CustomAutoPlace.js | 81 ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/flowable/flowableExtension.js | 72 ruoyi-ui/apps/web-antd/src/package/penal/signal-message/SignalAndMessage.vue | 110 ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/activiti/index.js | 10 ruoyi-ui/apps/web-antd/src/package/penal/PropertiesPanel.vue | 216 ruoyi-ui/apps/web-antd/src/package/utils.js | 71 ruoyi-ui/apps/web-antd/src/bootstrap.ts | 5 ruoyi-ui/apps/web-antd/src/package/penal/base/ElementBaseInfo.vue | 80 ruoyi-ui/apps/web-antd/src/package/penal/listeners/utilSelf.js | 81 ruoyi-ui/apps/web-antd/src/views/workflow/work/detail.vue | 545 ++ ruoyi-ui/apps/web-antd/src/package/penal/form/ElementForm.vue | 417 + ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/flowable/index.js | 10 ruoyi-ui/apps/web-antd/src/views/workflow/deploy/index.vue | 236 ruoyi-ui/apps/web-antd/src/components/ProcessViewer/index.vue | 237 ruoyi-ui/apps/web-antd/src/api/workflow/category/index.ts | 76 ruoyi-ui/apps/web-antd/src/modules/rules/CustomRules.js | 16 ruoyi-ui/apps/web-antd/src/package/designer/plugins/descriptor/camundaDescriptor.json | 1087 ++++ ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/activiti/activitiExtension.js | 73 ruoyi-ui/apps/web-antd/src/api/workflow/deploy/index.ts | 23 ruoyi-ui/apps/web-antd/src/package/theme/index.scss | 168 ruoyi-ui/apps/web-antd/src/views/workflow/model/index.vue | 438 + ruoyi-ui/apps/web-antd/src/package/designer/plugins/palette/index.js | 6 ruoyi-ui/apps/web-antd/src/package/designer/plugins/translate/zh.js | 238 ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/camunda/extension.js | 144 ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/camunda/index.js | 7 ruoyi-ui/apps/web-antd/src/views/workflow/work/finished.vue | 140 ruoyi-ui/apps/web-antd/src/modules/custom-renderer/index.js | 6 ruoyi-ui/apps/web-antd/src/package/designer/plugins/descriptor/activitiDescriptor.json | 1071 ++++ ruoyi-ui/apps/web-antd/src/package/theme/process-panel.scss | 129 ruoyi-ui/apps/web-antd/src/package/designer/plugins/content-pad/index.js | 6 ruoyi-ui/apps/web-antd/src/package/penal/index.js | 7 ruoyi-ui/apps/web-antd/src/package/palette/index.js | 7 ruoyi-ui/apps/web-antd/src/views/workflow/work/index.vue | 158 ruoyi-ui/apps/web-antd/src/modules/rules/index.js | 6 ruoyi-ui/apps/web-antd/src/package/designer/ProcessDesigner.vue | 519 ++ ruoyi-ui/apps/web-antd/src/package/penal/task/task-components/UserTask.vue | 684 ++ ruoyi-ui/apps/web-antd/src/package/index.js | 21 ruoyi-ui/apps/web-antd/src/package/palette/ProcessPalette.vue | 106 ruoyi-ui/apps/web-antd/src/package/penal/flow-condition/FlowCondition.vue | 142 ruoyi-ui/apps/web-antd/src/package/designer/index.js | 7 ruoyi-ui/apps/web-antd/package.json | 10 ruoyi-ui/apps/web-antd/src/api/workflow/work/process.ts | 61 ruoyi-ui/apps/web-antd/src/utils/min-dash.js | 694 ++ ruoyi-ui/apps/web-antd/src/views/workflow/work/copy.vue | 122 ruoyi-ui/apps/web-antd/src/views/workflow/listener/index.vue | 469 + ruoyi-ui/apps/web-antd/src/package/penal/other/ElementOtherConfig.vue | 59 ruoyi-ui/apps/web-antd/src/modules/custom-renderer/CustomRenderer.js | 17 ruoyi-ui/apps/web-antd/src/package/theme/process-designer.scss | 155 ruoyi-ui/apps/web-antd/src/package/designer/plugins/defaultEmpty.js | 24 ruoyi-ui/apps/web-antd/src/components/ProcessDesigner/index.vue | 214 79 files changed, 14,198 insertions(+), 201 deletions(-) diff --git a/ruoyi-ui/apps/web-antd/package.json b/ruoyi-ui/apps/web-antd/package.json index 7336cf0..a3bb4ed 100644 --- a/ruoyi-ui/apps/web-antd/package.json +++ b/ruoyi-ui/apps/web-antd/package.json @@ -27,6 +27,7 @@ }, "dependencies": { "@ant-design/icons-vue": "^7.0.1", + "@element-plus/icons-vue": "^2.3.1", "@form-create/designer": "^3.2.11", "@form-create/element-ui": "^3.2.25", "@tinymce/tinymce-vue": "^6.0.1", @@ -48,20 +49,27 @@ "ant-design-vue": "catalog:", "axios": "^1.10.0", "bpmn-js": "^18.6.2", + "bpmn-js-token-simulation": "^0.38.1", + "codemirror": "5.65.0", + "codemirror-editor-vue3": "^2.8.0", "cropperjs": "^1.6.2", "crypto-js": "^4.2.0", "dayjs": "catalog:", "diagram-js": "^15.3.0", + "diagram-js-minimap": "^5.2.0", "echarts": "^5.5.1", "element-plus": "^2.10.2", "jsencrypt": "^3.3.2", + "lodash": "^4.17.21", "lodash-es": "^4.17.21", + "min-dash": "^4.2.3", "pinia": "catalog:", "qs": "^6.13.1", "tinymce": "^7.3.0", "unplugin-vue-components": "^0.27.3", "vue": "catalog:", - "vue-router": "catalog:" + "vue-router": "catalog:", + "x2js": "^3.4.4" }, "devDependencies": { "@types/crypto-js": "^4.2.2", diff --git a/ruoyi-ui/apps/web-antd/src/api/workflow/category/index.ts b/ruoyi-ui/apps/web-antd/src/api/workflow/category/index.ts index 8126eff..4952772 100644 --- a/ruoyi-ui/apps/web-antd/src/api/workflow/category/index.ts +++ b/ruoyi-ui/apps/web-antd/src/api/workflow/category/index.ts @@ -1,63 +1,29 @@ -import type { - CategoryForm, - CategoryQuery, - CategoryTree, - CategoryVO, -} from './model'; - -import type { ID, IDS } from '#/api/common'; - +// import request from '#/utils/request'; import { requestClient } from '#/api/request'; - -/** - * 鑾峰彇娴佺▼鍒嗙被鏍戝垪琛� - * @returns tree - */ -export function categoryTree() { - return requestClient.get<CategoryTree[]>('/workflow/category/categoryTree'); +// 鏌ヨ娴佺▼鍒嗙被鍒楄〃 +export function listCategory(query) { + return requestClient.get('/category/list', { params: query }); } -/** - * 鏌ヨ娴佺▼鍒嗙被鍒楄〃 - * @param params - * @returns 娴佺▼鍒嗙被鍒楄〃 - */ -export function categoryList(params?: CategoryQuery) { - return requestClient.get<CategoryVO[]>(`/workflow/category/list`, { params }); +// 鏌ヨ娴佺▼鍒嗙被鍒楄〃 +export function listAllCategory(query) { + return requestClient.get('/category/listAll', { params: query }); +} +// 鏌ヨ娴佺▼鍒嗙被璇︾粏 +export function getCategory(categoryId) { + return requestClient.get(`/category/${categoryId}`); +} +// 鏂板娴佺▼鍒嗙被 +export function addCategory(data) { + return requestClient.post('/category', { data }); } -/** - * 鏌ヨ娴佺▼鍒嗙被璇︽儏 - * @param id id - * @returns 娴佺▼鍒嗙被璇︽儏 - */ -export function categoryInfo(id: ID) { - return requestClient.get<CategoryVO>(`/workflow/category/${id}`); +// 淇敼娴佺▼鍒嗙被 +export function updateCategory(data) { + return requestClient.put('/category', { data }); } -/** - * 鏂板娴佺▼鍒嗙被 - * @param data - * @returns void - */ -export function categoryAdd(data: CategoryForm) { - return requestClient.postWithMsg<void>('/workflow/category', data); -} - -/** - * 鏇存柊娴佺▼鍒嗙被 - * @param data - * @returns void - */ -export function categoryUpdate(data: CategoryForm) { - return requestClient.putWithMsg<void>('/workflow/category', data); -} - -/** - * 鍒犻櫎娴佺▼鍒嗙被 - * @param id id - * @returns void - */ -export function categoryRemove(id: ID | IDS) { - return requestClient.deleteWithMsg<void>(`/workflow/category/${id}`); +// 鍒犻櫎娴佺▼鍒嗙被 +export function delCategory(categoryIds) { + return requestClient.delete(`/category/${categoryIds}`); } diff --git a/ruoyi-ui/apps/web-antd/src/api/workflow/category/index1.ts b/ruoyi-ui/apps/web-antd/src/api/workflow/category/index1.ts new file mode 100644 index 0000000..8126eff --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/api/workflow/category/index1.ts @@ -0,0 +1,63 @@ +import type { + CategoryForm, + CategoryQuery, + CategoryTree, + CategoryVO, +} from './model'; + +import type { ID, IDS } from '#/api/common'; + +import { requestClient } from '#/api/request'; + +/** + * 鑾峰彇娴佺▼鍒嗙被鏍戝垪琛� + * @returns tree + */ +export function categoryTree() { + return requestClient.get<CategoryTree[]>('/workflow/category/categoryTree'); +} + +/** + * 鏌ヨ娴佺▼鍒嗙被鍒楄〃 + * @param params + * @returns 娴佺▼鍒嗙被鍒楄〃 + */ +export function categoryList(params?: CategoryQuery) { + return requestClient.get<CategoryVO[]>(`/workflow/category/list`, { params }); +} + +/** + * 鏌ヨ娴佺▼鍒嗙被璇︽儏 + * @param id id + * @returns 娴佺▼鍒嗙被璇︽儏 + */ +export function categoryInfo(id: ID) { + return requestClient.get<CategoryVO>(`/workflow/category/${id}`); +} + +/** + * 鏂板娴佺▼鍒嗙被 + * @param data + * @returns void + */ +export function categoryAdd(data: CategoryForm) { + return requestClient.postWithMsg<void>('/workflow/category', data); +} + +/** + * 鏇存柊娴佺▼鍒嗙被 + * @param data + * @returns void + */ +export function categoryUpdate(data: CategoryForm) { + return requestClient.putWithMsg<void>('/workflow/category', data); +} + +/** + * 鍒犻櫎娴佺▼鍒嗙被 + * @param id id + * @returns void + */ +export function categoryRemove(id: ID | IDS) { + return requestClient.deleteWithMsg<void>(`/workflow/category/${id}`); +} diff --git a/ruoyi-ui/apps/web-antd/src/api/workflow/deploy/index.ts b/ruoyi-ui/apps/web-antd/src/api/workflow/deploy/index.ts new file mode 100644 index 0000000..f1e4326 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/api/workflow/deploy/index.ts @@ -0,0 +1,23 @@ +// import request from '#/utils/request'; +import { requestClient } from '#/api/request'; +// 鏌ヨ娴佺▼閮ㄧ讲鍒楄〃 +export function listDeploy(query) { + return requestClient.get('/deploy/list', { params: query }); +} + +export function listPublish(query) { + return requestClient.get('/deploy/publishList', { params: query }); +} +// 鑾峰彇娴佺▼妯″瀷娴佺▼鍥� +export function getBpmnXml(definitionId) { + return requestClient.get(`/deploy/bpmnXml/${definitionId}`); +} +// 淇敼娴佺▼鐘舵�� +export function changeState(params) { + return requestClient.put('/model', { params: params }); +} + +// 鍒犻櫎娴佺▼閮ㄧ讲 +export function delDeploy(deployIds) { + return requestClient.delete(`/deploy/${deployIds}`); +} diff --git a/ruoyi-ui/apps/web-antd/src/api/workflow/form/index.ts b/ruoyi-ui/apps/web-antd/src/api/workflow/form/index.ts new file mode 100644 index 0000000..2c473b6 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/api/workflow/form/index.ts @@ -0,0 +1,24 @@ +// import request from '#/utils/request'; +import { requestClient } from '#/api/request'; +// 鏌ヨ娴佺▼琛ㄥ崟鍒楄〃 +export function listForm(query) { + return requestClient.get('/flowable/form/list', { params: query }); +} + +// 鏌ヨ娴佺▼琛ㄥ崟璇︾粏 +export function getForm(formId) { + return requestClient.get(`/form/${formId}`); +} +// 鏂板娴佺▼琛ㄥ崟 +export function addForm(data) { + return requestClient.post('/form', { data }); +} +// 淇敼娴佺▼琛ㄥ崟 +export function updateForm(data) { + return requestClient.put('/form', { data }); +} + +// 鍒犻櫎娴佺▼琛ㄥ崟 +export function delForm(formId) { + return requestClient.delete(`/form/${formId}`); +} diff --git a/ruoyi-ui/apps/web-antd/src/api/workflow/identity/index.ts b/ruoyi-ui/apps/web-antd/src/api/workflow/identity/index.ts new file mode 100644 index 0000000..3f22f96 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/api/workflow/identity/index.ts @@ -0,0 +1,11 @@ +// import request from '#/utils/request'; +import { requestClient } from '#/api/request'; +// 鏌ヨ娴佺▼妯″瀷淇℃伅 + +export function selectUser(query) { + return requestClient.get('/system/user/list', { params: query }); +} + +export function deptTreeSelect() { + return requestClient.get('/system/user/deptTree'); +} diff --git a/ruoyi-ui/apps/web-antd/src/api/workflow/listener/index.ts b/ruoyi-ui/apps/web-antd/src/api/workflow/listener/index.ts new file mode 100644 index 0000000..4480da5 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/api/workflow/listener/index.ts @@ -0,0 +1,45 @@ +// import request from '#/utils/request'; +import { requestClient } from '#/api/request'; +// 鍒嗛〉鏌ヨ娴佺▼鐩戝惉鍣� +export function queryListenerPage(query) { + return requestClient.get('/flowable/listener/queryPage', { params: query }); +} + +// 鍒楄〃鏌ヨ娴佺▼鐩戝惉鍣� +export function queryListenerList(query) { + return requestClient.get('/flowable/listener/queryList', { params: query }); +} + +// 璇︾粏鏌ヨ娴佺▼鐩戝惉鍣� +export function getListener(formId) { + return requestClient.get(`/flowable/listener/query/${formId}`); +} + +// 鏂板娴佺▼鐩戝惉鍣� +export function addListener(data) { + return requestClient.post('/flowable/listener/insert', { data }); +} +// 淇敼娴佺▼鐩戝惉鍣� +export function updateListener(data) { + return requestClient.post('/flowable/listener/update', { data }); +} + +// 鍒犻櫎娴佺▼鐩戝惉鍣� +export function delListener(listenerId) { + return requestClient.post(`/listener/delete/${listenerId}`); +} + +// 鏂板娴佺▼鐩戝惉鍣ㄥ瓧娈� +export function insertListenerFieldAPI(data) { + return requestClient.post('/listener/insertField', { data }); +} + +// 淇敼娴佺▼鐩戝惉鍣ㄥ瓧娈� +export function updateListenerFieldAPI(data) { + return requestClient.post('/listener/updateField', { data }); +} + +// 鍒犻櫎娴佺▼鐩戝惉鍣ㄥ瓧娈� +export function deleteListenerFieldAPI(fieldIds) { + return requestClient.post(`/listener/deleteField/${fieldIds}`); +} diff --git a/ruoyi-ui/apps/web-antd/src/api/workflow/model/index.ts b/ruoyi-ui/apps/web-antd/src/api/workflow/model/index.ts new file mode 100644 index 0000000..caaab58 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/api/workflow/model/index.ts @@ -0,0 +1,47 @@ +import { requestClient } from '#/api/request'; + +// 鏌ヨ娴佺▼妯″瀷淇℃伅 +export function listModel(query) { + return requestClient.get('/model/list', { params: query }); +} + +// 鏌ヨ鍘嗗彶娴佺▼妯″瀷淇℃伅 +export function historyModel(query) { + return requestClient.get('/model/historyList', { params: query }); +} + +export function getModel(modelId) { + return requestClient.get(`/model/${modelId}`); +} + +// 鏂板妯″瀷淇℃伅 +export function addModel(data) { + return requestClient.post('/model', { data }); +} + +// 淇敼妯″瀷淇℃伅 +export function updateModel(data) { + return requestClient.put('/model', { data }); +} + +// 淇濆瓨娴佺▼妯″瀷 +export function saveModel(data) { + return requestClient.post('/model/save', { data }); +} + +export function latestModel(params) { + return requestClient.post('/model/latest', { params }); +} + +export function delModel(modelIds) { + return requestClient.delete(`/model/${modelIds}`); +} + +export function deployModel(params) { + return requestClient.post('/model/deploy', { params }); +} + +// 鑾峰彇娴佺▼妯″瀷娴佺▼鍥� +export function getBpmnXml(modelId) { + return requestClient.get(`/model/bpmnXml/${modelId}`); +} diff --git a/ruoyi-ui/apps/web-antd/src/api/workflow/work/process.ts b/ruoyi-ui/apps/web-antd/src/api/workflow/work/process.ts new file mode 100644 index 0000000..adfdc49 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/api/workflow/work/process.ts @@ -0,0 +1,61 @@ +import { requestClient } from '#/api/request'; +// 鏌ヨ娴佺▼鍒楄〃 +export function listProcess(query) { + return requestClient.get('/process/list', { params: query }); +} + +// 鏌ヨ娴佺▼鍒楄〃 +export function getProcessForm(query) { + return requestClient.get('/process/getProcessForm', { params: query }); +} +// 閮ㄧ讲娴佺▼瀹炰緥 +export function startProcess(processDefId,data) { + return requestClient.post(`/process/start/${processDefId}`, { data }); +} +// 鍒犻櫎娴佺▼瀹炰緥 +export function delProcess(ids) { + return requestClient.delete(`/process/instance/${ids}`); +} + +// 鑾峰彇娴佺▼鍥� +export function getBpmnXml(processDefId) { + return requestClient.get(`/process/bpmnXml/${processDefId}`); +} + +export function detailProcess(query) { + return requestClient.get('/process/detail', { params: query }); +} + +// 鎴戠殑鍙戣捣鐨勬祦绋� +export function listOwnProcess(query) { + return requestClient.get('/process/ownList', { params: query }); +} +// 鎴戝緟鍔炵殑娴佺▼ +export function listTodoProcess(query) { + return requestClient.get('/process/todoList', { params: query }); +} +// 鎴戝緟绛剧殑娴佺▼ +export function listClaimProcess(query) { + return requestClient.get('/process/claimList', { params: query }); +} +// 鎴戝凡鍔炵殑娴佺▼ +export function listFinishedProcess(query) { + return requestClient({ + url: '/process/finishedList', + method: 'get', + params: query + }); +} +export function listFinishedProcess(query) { + return requestClient.get('/process/finishedList', { params: query }); +} + +// 鏌ヨ娴佺▼鎶勯�佸垪琛� +export function listCopyProcess(query) { + return requestClient.get('/process/copyList', { params: query }); +} + +// 鍙栨秷鐢宠 +export function stopProcess(data) { + return requestClient.post('/task/stopProcess', { data }); +} diff --git a/ruoyi-ui/apps/web-antd/src/api/workflow/work/task.ts b/ruoyi-ui/apps/web-antd/src/api/workflow/work/task.ts new file mode 100644 index 0000000..e6e23eb --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/api/workflow/work/task.ts @@ -0,0 +1,35 @@ +// import request from '#/utils/request'; +import { requestClient } from '#/api/request'; +// 瀹屾垚浠诲姟 +export function complete(data) { + return requestClient.post('/task/complete', { data }); +} + +// 濮旀淳浠诲姟 +export function delegate(data) { + return requestClient.post('/task/delegate', { data }); +} +// 杞姙浠诲姟 +export function transfer(data) { + return requestClient.post('/task/transfer', { data }); +} +// 閫�鍥炰换鍔� +export function returnTask(data) { + return requestClient.post('/task/return', { data }); +} +// 鎷掔粷浠诲姟 +export function rejectTask(data) { + return requestClient.post('/task/reject', { data }); +} +// 绛炬敹浠诲姟 +export function claimTask(data) { + return requestClient.post('/task/claim', { data }); +} +// 鍙��鍥炰换鍔″垪琛� +export function returnList(data) { + return requestClient.post('/task/returnList', { data }); +} +// 鎾ゅ洖浠诲姟 +export function revokeProcess(data) { + return requestClient.post('/task/revokeProcess', { data }); +} diff --git a/ruoyi-ui/apps/web-antd/src/bootstrap.ts b/ruoyi-ui/apps/web-antd/src/bootstrap.ts index adf3f37..59d0529 100644 --- a/ruoyi-ui/apps/web-antd/src/bootstrap.ts +++ b/ruoyi-ui/apps/web-antd/src/bootstrap.ts @@ -7,7 +7,7 @@ import { initStores } from '@vben/stores'; import '@vben/styles'; import '@vben/styles/antd'; - +import * as ElementPlusIconsVue from '@element-plus/icons-vue'; import { useTitle } from '@vueuse/core'; import ElementPlus from 'element-plus'; import 'element-plus/dist/index.css'; // 鏍峰紡鏂囦欢 @@ -66,6 +66,9 @@ app.use(formCreate); app.use(FcDesigner); app.use(Antd); + for (const [key, component] of Object.entries(ElementPlusIconsVue)) { + app.component(key, component); + } // 鍔ㄦ�佹洿鏂版爣棰� watchEffect(() => { if (preferences.app.dynamicTitle) { diff --git a/ruoyi-ui/apps/web-antd/src/components/ProcessDesigner/index.vue b/ruoyi-ui/apps/web-antd/src/components/ProcessDesigner/index.vue new file mode 100644 index 0000000..7cd288b --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/components/ProcessDesigner/index.vue @@ -0,0 +1,214 @@ +<template> + <div class="process-design" :style="'display: flex; height:' + height"> + <my-process-designer + v-model="xmlString" + v-bind="controlForm" + keyboard + ref="processDesigner" + :events="[ + 'element.click', + 'connection.added', + 'connection.removed', + 'connection.changed' + ]" + @element-click="elementClick" + @init-finished="initModeler" + @element-contextmenu="elementContextmenu" + @save="onSaveProcess" + /> + <my-process-penal :bpmn-modeler="modeler" :prefix="controlForm.prefix" class="process-panel" /> + + <!-- demo config --> +<!-- <div class="demo-control-bar">--> +<!-- <div class="open-model-button" @click="controlDrawerVisible = true"><el-icon><setting /></el-icon></div>--> +<!-- </div>--> +<!-- <el-drawer v-model="controlDrawerVisible" size="400px" title="鍋忓ソ璁剧疆" append-to-body destroy-on-close>--> +<!-- <el-form :model="controlForm" size="small" label-width="100px" class="control-form" @submit.prevent>--> +<!-- <el-form-item label="娴佺▼ID">--> +<!-- <el-input v-model="controlForm.processId" @change="reloadProcessDesigner(true)" />--> +<!-- </el-form-item>--> +<!-- <el-form-item label="娴佺▼鍚嶇О">--> +<!-- <el-input v-model="controlForm.processName" @change="reloadProcessDesigner(true)" />--> +<!-- </el-form-item>--> +<!-- <el-form-item label="娴佽浆妯℃嫙">--> +<!-- <el-switch v-model="controlForm.simulation" inactive-text="鍋滅敤" active-text="鍚敤" @change="reloadProcessDesigner()" />--> +<!-- </el-form-item>--> +<!-- <el-form-item label="绂佺敤鍙屽嚮">--> +<!-- <el-switch v-model="controlForm.labelEditing" inactive-text="鍋滅敤" active-text="鍚敤" @change="changeLabelEditingStatus" />--> +<!-- </el-form-item>--> +<!-- <el-form-item label="鑷畾涔夋覆鏌�">--> +<!-- <el-switch v-model="controlForm.labelVisible" inactive-text="鍋滅敤" active-text="鍚敤" @change="changeLabelVisibleStatus" />--> +<!-- </el-form-item>--> +<!-- <el-form-item label="娴佺▼寮曟搸">--> +<!-- <el-radio-group v-model="controlForm.prefix" @change="reloadProcessDesigner()">--> +<!-- <el-radio label="camunda">camunda</el-radio>--> +<!-- <el-radio label="flowable">flowable</el-radio>--> +<!-- <el-radio label="activiti">activiti</el-radio>--> +<!-- </el-radio-group>--> +<!-- </el-form-item>--> +<!-- <el-form-item label="宸ュ叿鏍�">--> +<!-- <el-radio-group v-model="controlForm.headerButtonSize">--> +<!-- <el-radio label="small">small</el-radio>--> +<!-- <el-radio label="default">default</el-radio>--> +<!-- <el-radio label="large">large</el-radio>--> +<!-- </el-radio-group>--> +<!-- </el-form-item>--> +<!-- <el-switch v-model="pageMode" active-text="dark" inactive-text="light" @change="changePageMode"></el-switch>--> +<!-- </el-form>--> +<!-- </el-drawer>--> + </div> +</template> + +<script> +import MyProcessDesigner from '#/package/designer'; +// import MyProcessPalette from '@/package/palette'; +import MyProcessPenal from '#/package/penal'; +// 鑷畾涔夋覆鏌擄紙闅愯棌浜� label 鏍囩锛� +import CustomRenderer from '#/modules/custom-renderer'; +// 鑷畾涔夊厓绱犻�変腑鏃剁殑寮瑰嚭鑿滃崟锛堜慨鏀� 榛樿浠诲姟 涓� 鐢ㄦ埛浠诲姟锛� +import CustomContentPadProvider from '#/package/designer/plugins/content-pad'; +// 鑷畾涔夊乏渚ц彍鍗曪紙淇敼 榛樿浠诲姟 涓� 鐢ㄦ埛浠诲姟锛� +import CustomPaletteProvider from '#/package/designer/plugins/palette'; + +import '#/package/theme/index.scss'; +import 'bpmn-js/dist/assets/diagram-js.css'; +import 'bpmn-js/dist/assets/bpmn-font/css/bpmn.css'; +import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css'; + +export default { + name: 'ProcessDesigner', + props: { + bpmnXml: { + type: String, + required: true + }, + designerForm: { + type: Object, + required: true + } + }, + components: { + MyProcessDesigner, + MyProcessPenal + }, + data () { + return { + height: document.documentElement.clientHeight - 94.5 + "px;", + xmlString: this.bpmnXml, + modeler: null, + controlDrawerVisible: false, + infoTipVisible: false, + pageMode: false, + controlForm: { + processId: this.designerForm.processKey || '', + processName: this.designerForm.processName || '', + simulation: true, + labelEditing: false, + labelVisible: false, + prefix: 'flowable', + headerButtonSize: 'default', + additionalModel: [CustomContentPadProvider, CustomPaletteProvider] + }, + addis: { + CustomContentPadProvider, + CustomPaletteProvider + } + } + }, + methods: { + reloadProcessDesigner(notDeep) { + this.controlForm.additionalModel = []; + for (const key in this.addis) { + if (this.addis[key]) { + this.controlForm.additionalModel.push(this.addis[key]); + } + } + !notDeep && (this.xmlString = undefined); + this.reloadIndex += 1; + this.modeler = null; // 閬垮厤 panel 寮傚父 + }, + changeLabelEditingStatus(status) { + this.addis.labelEditing = status ? { labelEditingProvider: ['value', ''] } : false; + this.reloadProcessDesigner(); + }, + changeLabelVisibleStatus(status) { + this.addis.customRenderer = status ? CustomRenderer : false; + this.reloadProcessDesigner(); + }, + elementClick(element) { + this.element = element; + }, + initModeler(modeler) { + setTimeout(() => { + this.modeler = modeler; + }, 10); + }, + elementContextmenu(element) { + }, + onSaveProcess(saveData) { + this.$emit('save', saveData); + } + } +} +</script> + +<style lang="scss"> +body { + overflow: hidden; + margin: 0; + box-sizing: border-box; +} +body, +body * { + /* 婊氬姩鏉� */ + &::-webkit-scrollbar-track-piece { + background-color: #fff; /*婊氬姩鏉$殑鑳屾櫙棰滆壊*/ + -webkit-border-radius: 0; /*婊氬姩鏉$殑鍦嗚瀹藉害*/ + } + + &::-webkit-scrollbar { + width: 10px; /*婊氬姩鏉$殑瀹藉害*/ + height: 8px; /*婊氬姩鏉$殑楂樺害*/ + } + + &::-webkit-scrollbar-thumb:vertical { + /*鍨傜洿婊氬姩鏉$殑鏍峰紡*/ + height: 50px; + background-color: rgba(153, 153, 153, 0.5); + -webkit-border-radius: 4px; + outline: 2px solid #fff; + outline-offset: -2px; + border: 2px solid #fff; + } + + &::-webkit-scrollbar-thumb { + /*婊氬姩鏉$殑hover鏍峰紡*/ + background-color: rgba(159, 159, 159, 0.3); + -webkit-border-radius: 4px; + } + + &::-webkit-scrollbar-thumb:hover { + /*婊氬姩鏉$殑hover鏍峰紡*/ + background-color: rgba(159, 159, 159, 0.5); + -webkit-border-radius: 4px; + } +} +.demo-control-bar { + position: fixed; + right: 8px; + bottom: 48px; + z-index: 1; +} +.open-model-button { + width: 48px; + height: 48px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 4px; + font-size: 32px; + background: rgba(64, 158, 255, 1); + color: #ffffff; + cursor: pointer; +} +</style> diff --git a/ruoyi-ui/apps/web-antd/src/components/ProcessViewer/index.vue b/ruoyi-ui/apps/web-antd/src/components/ProcessViewer/index.vue new file mode 100644 index 0000000..24ce40f --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/components/ProcessViewer/index.vue @@ -0,0 +1,237 @@ +<template> + <div class="process-viewer"> + <div class="process-canvas" style="height: 100%;" ref="processCanvas" v-show="!isLoading" /> + <!-- 鑷畾涔夌澶存牱寮忥紝鐢ㄤ簬鎴愬姛鐘舵�佷笅娴佺▼杩炵嚎绠ご --> + <defs ref="customSuccessDefs"> + <marker id="sequenceflow-end-white-success" viewBox="0 0 20 20" refX="11" refY="10" markerWidth="10" markerHeight="10" orient="auto"> + <path class="success-arrow" d="M 1 5 L 11 10 L 1 15 Z" style="stroke-width: 1px; stroke-linecap: round; stroke-dasharray: 10000, 1;"></path> + </marker> + <marker id="conditional-flow-marker-white-success" viewBox="0 0 20 20" refX="-1" refY="10" markerWidth="10" markerHeight="10" orient="auto"> + <path class="success-conditional" d="M 0 10 L 8 6 L 16 10 L 8 14 Z" style="stroke-width: 1px; stroke-linecap: round; stroke-dasharray: 10000, 1;"></path> + </marker> + </defs> + <!-- 鑷畾涔夌澶存牱寮忥紝鐢ㄤ簬澶辫触鐘舵�佷笅娴佺▼杩炵嚎绠ご --> + <defs ref="customFailDefs"> + <marker id="sequenceflow-end-white-fail" viewBox="0 0 20 20" refX="11" refY="10" markerWidth="10" markerHeight="10" orient="auto"> + <path class="fail-arrow" d="M 1 5 L 11 10 L 1 15 Z" style="stroke-width: 1px; stroke-linecap: round; stroke-dasharray: 10000, 1;"></path> + </marker> + <marker id="conditional-flow-marker-white-fail" viewBox="0 0 20 20" refX="-1" refY="10" markerWidth="10" markerHeight="10" orient="auto"> + <path class="fail-conditional" d="M 0 10 L 8 6 L 16 10 L 8 14 Z" style="stroke-width: 1px; stroke-linecap: round; stroke-dasharray: 10000, 1;"></path> + </marker> + </defs> + <!-- 宸插畬鎴愯妭鐐规偓娴脊绐� --> + <el-dialog class="comment-dialog" :title="dlgTitle || '瀹℃壒璁板綍'" v-model="dialogVisible"> + <el-row> + <el-table :data="taskCommentList" border header-cell-class-name="table-header-gray"> + <el-table-column label="搴忓彿" header-align="center" align="center" type="index" width="55px" /> + <el-table-column label="鍊欓�夊姙鐞�" prop="candidate" width="150px" align="center" /> + <el-table-column label="瀹為檯鍔炵悊" prop="assigneeName" width="100px" align="center" /> + <el-table-column label="澶勭悊鏃堕棿" prop="createTime" width="140px" align="center" /> + <el-table-column label="鍔炵粨鏃堕棿" prop="finishTime" width="140px" align="center" /> + <el-table-column label="鑰楁椂" prop="duration" width="100px" align="center" /> + <el-table-column label="瀹℃壒鎰忚" align="center"> + <template #default="scope"> + {{scope.row.commentList&&scope.row.commentList[0]?scope.row.commentList[0].fullMessage:''}} + </template> + </el-table-column> + </el-table> + </el-row> + </el-dialog> + <div style="position: absolute; top: 0; left: 0; width: 100%;"> + <el-row type="flex" justify="end"> + <el-button-group key="scale-control"> + <el-button :plain="true" :disabled="defaultZoom <= 0.3" icon="ZoomOut" @click="processZoomOut()" /> + <el-button style="width: 90px;">{{ Math.floor(this.defaultZoom * 10 * 10) + "%" }}</el-button> + <el-button :plain="true" :disabled="defaultZoom >= 3.9" icon="ZoomIn" @click="processZoomIn()" /> + <el-button icon="ScaleToOriginal" @click="processReZoom()" /> + <slot /> + </el-button-group> + </el-row> + </div> + </div> +</template> + +<script> +import '#/package/theme/index.scss'; +import BpmnViewer from 'bpmn-js/lib/Viewer'; +import MoveCanvasModule from 'diagram-js/lib/navigation/movecanvas'; + +export default { + props: { + xml: { + type: String + }, + finishedInfo: { + type: Object + }, + // 鎵�鏈夎妭鐐瑰鎵硅褰� + allCommentList: { + type: Array + } + }, + data () { + return { + dialogVisible: false, + dlgTitle: undefined, + defaultZoom: 1, + // 鏄惁姝e湪鍔犺浇娴佺▼鍥� + isLoading: false, + bpmnViewer: undefined, + // 宸插畬鎴愭祦绋嬪厓绱� + processNodeInfo: undefined, + // 褰撳墠浠诲姟id + selectTaskId: undefined, + // 浠诲姟鑺傜偣瀹℃壒璁板綍 + taskCommentList: [], + // 宸插畬鎴愪换鍔℃偓娴欢杩烼imer + hoverTimer: null + } + }, + watch: { + xml: { + handler(newXml) { + this.importXML(newXml); + }, + immediate: true + }, + finishedInfo: { + handler(newInfo) { + this.setProcessStatus(newInfo); + }, + immediate: true + } + }, + created() { + this.$nextTick(() => { + this.importXML(this.xml) + this.setProcessStatus(this.finishedInfo); + }) + }, + methods: { + processReZoom() { + this.defaultZoom = 1; + this.bpmnViewer.get('canvas').zoom('fit-viewport', 'auto'); + }, + processZoomIn(zoomStep = 0.1) { + let newZoom = Math.floor(this.defaultZoom * 100 + zoomStep * 100) / 100; + if (newZoom > 4) { + throw new Error('[Process Designer Warn ]: The zoom ratio cannot be greater than 4'); + } + this.defaultZoom = newZoom; + this.bpmnViewer.get('canvas').zoom(this.defaultZoom); + }, + processZoomOut(zoomStep = 0.1) { + let newZoom = Math.floor(this.defaultZoom * 100 - zoomStep * 100) / 100; + if (newZoom < 0.2) { + throw new Error('[Process Designer Warn ]: The zoom ratio cannot be less than 0.2'); + } + this.defaultZoom = newZoom; + this.bpmnViewer.get('canvas').zoom(this.defaultZoom); + }, + // 娴佺▼鍥鹃瑙堟竻绌� + clearViewer() { + if (this.$refs.processCanvas) { + this.$refs.processCanvas.innerHTML = ''; + } + if (this.bpmnViewer) { + this.bpmnViewer.destroy(); + } + this.bpmnViewer = null; + }, + // 娣诲姞鑷畾涔夌澶� + addCustomDefs() { + const canvas = this.bpmnViewer.get('canvas'); + const svg = canvas._svg; + const customSuccessDefs = this.$refs.customSuccessDefs; + const customFailDefs = this.$refs.customFailDefs; + svg.appendChild(customSuccessDefs); + svg.appendChild(customFailDefs); + }, + // 浠诲姟鎮诞寮圭獥 + onSelectElement(element) { + this.selectTaskId = undefined; + this.dlgTitle = undefined; + + if (this.processNodeInfo == null || this.processNodeInfo.finishedTaskSet == null) return; + + if (element == null || this.processNodeInfo.finishedTaskSet.indexOf(element.id) === -1) { + return; + } + + this.selectTaskId = element.id; + this.dlgTitle = element.businessObject ? element.businessObject.name : undefined; + // 璁$畻褰撳墠鎮诞浠诲姟瀹℃壒璁板綍锛屽鏋滆褰曚负绌轰笉鏄剧ず寮圭獥 + this.taskCommentList = (this.allCommentList || []).filter(item => { + return item.activityId === this.selectTaskId; + }); + this.dialogVisible = true; + }, + // 鏄剧ず娴佺▼鍥� + async importXML(xml) { + this.clearViewer(); + if (xml != null && xml !== '') { + try { + this.bpmnViewer = new BpmnViewer({ + additionalModules: [ + // 绉诲姩鏁翠釜鐢诲竷 + MoveCanvasModule + ], + container: this.$refs.processCanvas, + }); + // 浠诲姟鑺傜偣鎮诞浜嬩欢 + this.bpmnViewer.on('element.click', ({ element }) => { + this.onSelectElement(element); + }); + + this.isLoading = true; + await this.bpmnViewer.importXML(xml); + this.addCustomDefs(); + } catch (e) { + this.clearViewer(); + } finally { + this.isLoading = false; + this.setProcessStatus(this.processNodeInfo); + } + } + }, + // 璁剧疆娴佺▼鍥惧厓绱犵姸鎬� + setProcessStatus (processNodeInfo) { + this.processNodeInfo = processNodeInfo; + if (this.isLoading || this.processNodeInfo == null || this.bpmnViewer == null) return; + let { finishedTaskSet, rejectedTaskSet, unfinishedTaskSet, finishedSequenceFlowSet } = this.processNodeInfo; + const canvas = this.bpmnViewer.get('canvas'); + const elementRegistry = this.bpmnViewer.get('elementRegistry'); + if (Array.isArray(finishedSequenceFlowSet)) { + finishedSequenceFlowSet.forEach(item => { + if (item != null) { + canvas.addMarker(item, 'success'); + let element = elementRegistry.get(item); + const conditionExpression = element.businessObject.conditionExpression; + if (conditionExpression) { + canvas.addMarker(item, 'condition-expression'); + } + } + }); + } + if (Array.isArray(finishedTaskSet)) { + finishedTaskSet.forEach(item => canvas.addMarker(item, 'success')); + } + if (Array.isArray(unfinishedTaskSet)) { + unfinishedTaskSet.forEach(item => canvas.addMarker(item, 'primary')); + } + if (Array.isArray(rejectedTaskSet)) { + rejectedTaskSet.forEach(item => { + if (item != null) { + let element = elementRegistry.get(item); + if (element.type.includes('Task')) { + canvas.addMarker(item, 'danger'); + } else { + canvas.addMarker(item, 'warning'); + } + } + }) + } + } + } +} +</script> diff --git a/ruoyi-ui/apps/web-antd/src/modules/auto-place/CustomAutoPlace.js b/ruoyi-ui/apps/web-antd/src/modules/auto-place/CustomAutoPlace.js new file mode 100644 index 0000000..64a1560 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/modules/auto-place/CustomAutoPlace.js @@ -0,0 +1,81 @@ +import AutoPlace from 'diagram-js/lib/features/auto-place/AutoPlace'; + +export default function CustomAutoPlace(eventBus, modeling) { + AutoPlace.call(this, eventBus, modeling, 3000); + + eventBus.on('autoPlace', 3000, function(context) { + const shape = context.shape; + const source = context.source; + + return getNewCustomShapePosition(source, shape); + }); + + this.append = function(source, shape, hints) { + eventBus.fire('autoPlace.start', { + source: source, + shape: shape + }); + + // allow others to provide the position + var position = eventBus.fire('autoPlace', { + source: source, + shape: shape + }); + + console.log('hints', hints, 'position', position); + + var newShape = modeling.appendShape(source, shape, position, source.parent, hints); + + eventBus.fire('autoPlace.end', { + source: source, + shape: newShape + }); + + return newShape; + }; +} + +export function asTRBL(bounds) { + return { + top: bounds.y, + right: bounds.x + (bounds.width || 0), + bottom: bounds.y + (bounds.height || 0), + left: bounds.x + }; +} + +export function roundPoint(point) { + return { + x: Math.round(point.x), + y: Math.round(point.y) + }; +} + +export function getMid(bounds) { + return roundPoint({ + x: bounds.x + (bounds.width || 0) / 2, + y: bounds.y + (bounds.height || 0) / 2 + }); +} + +export function getNewCustomShapePosition(source, element, hints) { + if (!hints) { + hints = {}; + } + + var distance = hints.defaultDistance || 50; + + var sourceMid = getMid(source); + var sourceTrbl = asTRBL(source); + + // simply put element right next to source + return { + x: sourceMid.x, + y: sourceTrbl.bottom + distance + element.height / 2 + }; +} + +const F = function() {}; // 鏍稿績锛屽埄鐢ㄧ┖瀵硅薄浣滀负涓粙锛� +F.prototype = AutoPlace.prototype; // 鏍稿績锛屽皢鐖剁被鐨勫師鍨嬭祴鍊肩粰绌哄璞锛� +CustomAutoPlace.prototype = new F(); // 鏍稿績锛屽皢 F鐨勫疄渚嬭祴鍊肩粰瀛愮被锛� +CustomAutoPlace.prototype.constructor = AutoPlace; // 淇瀛愮被CustomRenderer鐨勬瀯閫犲櫒鎸囧悜锛岄槻姝㈠師鍨嬮摼鐨勬贩涔憋紱 diff --git a/ruoyi-ui/apps/web-antd/src/modules/auto-place/index.js b/ruoyi-ui/apps/web-antd/src/modules/auto-place/index.js new file mode 100644 index 0000000..44dc28e --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/modules/auto-place/index.js @@ -0,0 +1,6 @@ +import CustomAutoPlace from './CustomAutoPlace'; + +export default { + __init__: ['autoPlace'], + autoPlace: ['type', CustomAutoPlace] +}; diff --git a/ruoyi-ui/apps/web-antd/src/modules/custom-renderer/CustomRenderer.js b/ruoyi-ui/apps/web-antd/src/modules/custom-renderer/CustomRenderer.js new file mode 100644 index 0000000..6e9988b --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/modules/custom-renderer/CustomRenderer.js @@ -0,0 +1,17 @@ +import BpmnRenderer from 'bpmn-js/lib/draw/BpmnRenderer'; + +export default function CustomRenderer(eventBus, styles, pathMap, canvas, textRenderer) { + const config = { + defaultFillColor: '', + defaultStrokeColor: '#8b238f', + defaultLabelColor: '#2dd257' + }; + BpmnRenderer.call(this, config, eventBus, styles, pathMap, canvas, textRenderer, 2000); +} + +CustomRenderer.$inject = ['eventBus', 'styles', 'pathMap', 'canvas', 'textRenderer']; + +const F = function() {}; // 鏍稿績锛屽埄鐢ㄧ┖瀵硅薄浣滀负涓粙锛� +F.prototype = BpmnRenderer.prototype; // 鏍稿績锛屽皢鐖剁被鐨勫師鍨嬭祴鍊肩粰绌哄璞锛� +CustomRenderer.prototype = new F(); // 鏍稿績锛屽皢 F鐨勫疄渚嬭祴鍊肩粰瀛愮被锛� +CustomRenderer.prototype.constructor = CustomRenderer; // 淇瀛愮被CustomRenderer鐨勬瀯閫犲櫒鎸囧悜锛岄槻姝㈠師鍨嬮摼鐨勬贩涔憋紱 diff --git a/ruoyi-ui/apps/web-antd/src/modules/custom-renderer/index.js b/ruoyi-ui/apps/web-antd/src/modules/custom-renderer/index.js new file mode 100644 index 0000000..ab0b4ef --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/modules/custom-renderer/index.js @@ -0,0 +1,6 @@ +import CustomRenderer from './CustomRenderer'; + +export default { + __init__: ['customRenderer'], + customRenderer: ['type', CustomRenderer] +}; diff --git a/ruoyi-ui/apps/web-antd/src/modules/rules/CustomRules.js b/ruoyi-ui/apps/web-antd/src/modules/rules/CustomRules.js new file mode 100644 index 0000000..4f659cc --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/modules/rules/CustomRules.js @@ -0,0 +1,16 @@ +import BpmnRules from 'bpmn-js/lib/features/rules/BpmnRules'; +import inherits from 'inherits'; + +export default function CustomRules(eventBus) { + BpmnRules.call(this, eventBus); +} + +inherits(CustomRules, BpmnRules); + +CustomRules.prototype.canDrop = function() { + return false; +}; + +CustomRules.prototype.canMove = function() { + return false; +}; diff --git a/ruoyi-ui/apps/web-antd/src/modules/rules/index.js b/ruoyi-ui/apps/web-antd/src/modules/rules/index.js new file mode 100644 index 0000000..bc0a21f --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/modules/rules/index.js @@ -0,0 +1,6 @@ +import CustomRules from './CustomRules'; + +export default { + __init__: ['customRules'], + customRules: ['type', CustomRules] +}; diff --git a/ruoyi-ui/apps/web-antd/src/package/Log.js b/ruoyi-ui/apps/web-antd/src/package/Log.js new file mode 100644 index 0000000..f1605fd --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/Log.js @@ -0,0 +1,99 @@ +function Log() {} + +Log.prototype.type = ['primary', 'success', 'warn', 'error', 'info']; + +Log.prototype.typeColor = function(type) { + let color = ''; + switch (type) { + case 'primary': + color = '#2d8cf0'; + break; + case 'success': + color = '#19be6b'; + break; + case 'info': + color = '#909399'; + break; + case 'warn': + color = '#ff9900'; + break; + case 'error': + color = '#f03f14'; + break; + default: + color = '#35495E'; + break; + } + return color; +}; + +Log.prototype.isArray = function(obj) { + return Object.prototype.toString.call(obj) === '[object Array]'; +}; + +Log.prototype.print = function(text, type = 'default', back = false) { + if (typeof text === 'object') { + // 濡傛灉鏄皪璞″墖瑾跨敤鎵撳嵃灏嶈薄鏂瑰紡 + this.isArray(text) ? console.table(text) : console.dir(text); + return; + } + if (back) { + // 濡傛灉鏄墦鍗板付鑳屾櫙鍦栫殑 + console.log(`%c ${text} `, `background:${this.typeColor(type)}; padding: 2px; border-radius: 4px; color: #fff;`); + } else { + console.log( + `%c ${text} `, + `border: 1px solid ${this.typeColor(type)}; + padding: 2px; border-radius: 4px; + color: ${this.typeColor(type)};` + ); + } +}; + +Log.prototype.printBack = function(type = 'primary', title) { + this.print(type, title, true); +}; + +Log.prototype.pretty = function(type = 'primary', title, text) { + if (typeof text === 'object') { + console.group('Console Group', title); + console.log( + `%c ${title}`, + `background:${this.typeColor(type)};border:1px solid ${this.typeColor(type)}; + padding: 1px; border-radius: 4px; color: #fff;` + ); + this.isArray(text) ? console.table(text) : console.dir(text); + console.groupEnd(); + return; + } + console.log( + `%c ${title} %c ${text} %c`, + `background:${this.typeColor(type)};border:1px solid ${this.typeColor(type)}; + padding: 1px; border-radius: 4px 0 0 4px; color: #fff;`, + `border:1px solid ${this.typeColor(type)}; + padding: 1px; border-radius: 0 4px 4px 0; color: ${this.typeColor(type)};`, + 'background:transparent' + ); +}; + +Log.prototype.prettyPrimary = function(title, ...text) { + text.forEach(t => this.pretty('primary', title, t)); +}; + +Log.prototype.prettySuccess = function(title, ...text) { + text.forEach(t => this.pretty('success', title, t)); +}; + +Log.prototype.prettyWarn = function(title, ...text) { + text.forEach(t => this.pretty('warn', title, t)); +}; + +Log.prototype.prettyError = function(title, ...text) { + text.forEach(t => this.pretty('error', title, t)); +}; + +Log.prototype.prettyInfo = function(title, ...text) { + text.forEach(t => this.pretty('info', title, t)); +}; + +export default new Log(); diff --git a/ruoyi-ui/apps/web-antd/src/package/designer/ProcessDesigner.vue b/ruoyi-ui/apps/web-antd/src/package/designer/ProcessDesigner.vue new file mode 100644 index 0000000..a343aa0 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/designer/ProcessDesigner.vue @@ -0,0 +1,519 @@ +<template> + <div class="my-process-designer"> + <div class="my-process-designer__header"> + <slot name="control-header"></slot> + <template v-if="!$slots['control-header']"> + <el-button-group key="file-control"> + <el-button :size="headerButtonSize" :type="headerButtonType" icon="el-icon-edit-outline" @click="onSave">淇濆瓨娴佺▼</el-button> + <el-button :size="headerButtonSize" :type="headerButtonType" :icon="FolderOpened" @click="$refs.refFile.click()">鎵撳紑鏂囦欢</el-button> + <el-tooltip effect="light"> + <template #content> + <a :size="headerButtonSize" @click="downloadProcessAsXml()" class="link-type">涓嬭浇涓篨ML鏂囦欢</a> + <br /> + <a :size="headerButtonSize" type="text" @click="downloadProcessAsSvg()" class="link-type">涓嬭浇涓篠VG鏂囦欢</a> + <br /> + <a :size="headerButtonSize" type="text" @click="downloadProcessAsBpmn()" class="link-type">涓嬭浇涓築PMN鏂囦欢</a> + </template> + <el-button :size="headerButtonSize" :type="headerButtonType" :icon="Download">涓嬭浇鏂囦欢</el-button> + </el-tooltip> + <el-tooltip effect="light"> + <template #content> + <a :size="headerButtonSize" type="text" @click="previewProcessXML" class="link-type">棰勮XML</a> + <br /> + <a :size="headerButtonSize" type="text" @click="previewProcessJson" class="link-type">棰勮JSON</a> + </template> + <el-button :size="headerButtonSize" :type="headerButtonType" :icon="View">棰勮</el-button> + </el-tooltip> + <el-tooltip v-if="simulation" effect="light" :content="this.simulationStatus ? '閫�鍑烘ā鎷�' : '寮�鍚ā鎷�'"> + <el-button :size="headerButtonSize" :type="headerButtonType" :icon="Cpu" @click="processSimulation"> + 妯℃嫙 + </el-button> + </el-tooltip> + </el-button-group> + <el-button-group key="align-control"> + <el-tooltip effect="light" content="鍚戝乏瀵归綈"> + <el-button :size="headerButtonSize" class="align align-left" :icon="Histogram" @click="elementsAlign('left')" /> + </el-tooltip> + <el-tooltip effect="light" content="鍚戝彸瀵归綈"> + <el-button :size="headerButtonSize" class="align align-right" :icon="Histogram" @click="elementsAlign('right')" /> + </el-tooltip> + <el-tooltip effect="light" content="鍚戜笂瀵归綈"> + <el-button :size="headerButtonSize" class="align align-top" :icon="Histogram" @click="elementsAlign('top')" /> + </el-tooltip> + <el-tooltip effect="light" content="鍚戜笅瀵归綈"> + <el-button :size="headerButtonSize" class="align align-bottom" :icon="Histogram" @click="elementsAlign('bottom')" /> + </el-tooltip> + <el-tooltip effect="light" content="姘村钩灞呬腑"> + <el-button :size="headerButtonSize" class="align align-center" :icon="Histogram" @click="elementsAlign('center')" /> + </el-tooltip> + <el-tooltip effect="light" content="鍨傜洿灞呬腑"> + <el-button :size="headerButtonSize" class="align align-middle" :icon="Histogram" @click="elementsAlign('middle')" /> + </el-tooltip> + </el-button-group> + <el-button-group key="scale-control"> + <el-tooltip effect="light" content="缂╁皬瑙嗗浘"> + <el-button :size="headerButtonSize" :disabled="defaultZoom < 0.2" :icon="ZoomOut" @click="processZoomOut()" /> + </el-tooltip> + <el-button :size="headerButtonSize">{{ Math.floor(this.defaultZoom * 10 * 10) + "%" }}</el-button> + <el-tooltip effect="light" content="鏀惧ぇ瑙嗗浘"> + <el-button :size="headerButtonSize" :disabled="defaultZoom > 4" :icon="ZoomIn" @click="processZoomIn()" /> + </el-tooltip> + <el-tooltip effect="light" content="閲嶇疆瑙嗗浘骞跺眳涓�"> + <el-button :size="headerButtonSize" :icon="ScaleToOriginal" @click="processReZoom()" /> + </el-tooltip> + </el-button-group> + <el-button-group key="stack-control"> + <el-tooltip effect="light" content="鎾ら攢"> + <el-button :size="headerButtonSize" :disabled="!revocable" :icon="RefreshLeft" @click="processUndo()" /> + </el-tooltip> + <el-tooltip effect="light" content="鎭㈠"> + <el-button :size="headerButtonSize" :disabled="!recoverable" :icon="RefreshRight" @click="processRedo()" /> + </el-tooltip> + <el-tooltip effect="light" content="閲嶆柊缁樺埗"> + <el-button :size="headerButtonSize" :icon="Refresh" @click="processRestart" /> + </el-tooltip> + </el-button-group> + </template> + <!-- 鐢ㄤ簬鎵撳紑鏈湴鏂囦欢--> + <input type="file" id="files" ref="refFile" style="display: none" accept=".xml, .bpmn" @change="importLocalFile" /> + </div> + <div class="my-process-designer__container"> + <div class="my-process-designer__canvas" ref="bpmn-canvas"></div> + </div> + <el-dialog :title="`棰勮${previewType}`" width="60%" v-model="previewModelVisible" append-to-body destroy-on-close> + <Codemirror + v-model:value="previewResult" + :options="cmOptions" + border + :height="600" + /> + </el-dialog> + </div> +</template> + +<script> +import BpmnModeler from "bpmn-js/lib/Modeler"; +import DefaultEmptyXML from "./plugins/defaultEmpty"; +// 缈昏瘧鏂规硶 +import customTranslate from "./plugins/translate/customTranslate"; +import translationsCN from "./plugins/translate/zh"; +// 妯℃嫙娴佽浆娴佺▼ +import tokenSimulation from "bpmn-js-token-simulation"; +// 鏍囩瑙f瀽鏋勫缓鍣� +// import bpmnPropertiesProvider from "bpmn-js-properties-panel/lib/provider/bpmn"; +// 鏍囩瑙f瀽 Moddle +import camundaModdleDescriptor from './plugins/descriptor/camundaDescriptor.json'; +import activitiModdleDescriptor from './plugins/descriptor/activitiDescriptor.json'; +import flowableModdleDescriptor from './plugins/descriptor/flowableDescriptor.json'; +// 鏍囩瑙f瀽 Extension +import camundaModdleExtension from './plugins/extension-moddle/camunda'; +import activitiModdleExtension from './plugins/extension-moddle/activiti'; +import flowableModdleExtension from './plugins/extension-moddle/flowable'; +// 寮曞叆json杞崲涓庨珮浜� +// import convert from "xml-js"; +import X2JS from "x2js"; + +import Codemirror from 'codemirror-editor-vue3'; +import 'codemirror/theme/monokai.css' +import 'codemirror/mode/javascript/javascript.js'; +import 'codemirror/mode/xml/xml.js'; + +export default { + name: "MyProcessDesigner", + componentName: "MyProcessDesigner", + components: { + Codemirror + }, + setup() { + return { + Histogram, Cpu, Refresh, RefreshLeft, RefreshRight, ZoomOut, ZoomIn, View, Download, FolderOpened, ScaleToOriginal + } + }, + emits: ['destroy', 'init-finished', 'commandStack-changed', 'update:modelValue', 'change', 'canvas-viewbox-changed', 'element-click',"save","connection-added"], + props: { + modelValue: String, // xml 瀛楃涓� + processId: String, + processName: String, + translations: Object, // 鑷畾涔夌殑缈昏瘧鏂囦欢 + options: { + type: Object, + default: () => ({}) + }, // 鑷畾涔夌殑缈昏瘧鏂囦欢 + additionalModel: [Object, Array], // 鑷畾涔塵odel + moddleExtension: Object, // 鑷畾涔塵oddle + onlyCustomizeAddi: { + type: Boolean, + default: false + }, + onlyCustomizeModdle: { + type: Boolean, + default: false + }, + simulation: { + type: Boolean, + default: true + }, + keyboard: { + type: Boolean, + default: true + }, + prefix: { + type: String, + default: "camunda" + }, + events: { + type: Array, + default: () => ["element.click"] + }, + headerButtonSize: { + type: String, + default: "small", + validator: value => ["default", "medium", "small", "mini"].indexOf(value) !== -1 + }, + headerButtonType: { + type: String, + default: "primary", + validator: value => ["default", "primary", "success", "warning", "danger", "info"].indexOf(value) !== -1 + } + }, + data() { + return { + defaultZoom: 1, + previewModelVisible: false, + simulationStatus: false, + previewResult: "", + previewType: "xml", + recoverable: false, + revocable: false, + cmOptions: { + mode: 'xml', // 璇█妯″紡 + theme: 'monokai', // 涓婚 + lineNumbers: true, // 鏄剧ず琛屽彿 + smartIndent: true, // 鏅鸿兘缂╄繘 + readOnly: true, + indentUnit: 2, // 鏅鸿兘缂╄繘鍗曚綅涓�4涓┖鏍奸暱搴� + foldGutter: true, // 鍚敤琛屾Ы涓殑浠g爜鎶樺彔 + styleActiveLine: true // 鏄剧ず閫変腑琛岀殑鏍峰紡 + } + }; + }, + computed: { + additionalModules() { + const Modules = []; + // 浠呬繚鐣欑敤鎴疯嚜瀹氫箟鎵╁睍妯″潡 + if (this.onlyCustomizeAddi) { + if (Object.prototype.toString.call(this.additionalModel) === "[object Array]") { + return this.additionalModel || []; + } + return [this.additionalModel]; + } + + // 鎻掑叆鐢ㄦ埛鑷畾涔夋墿灞曟ā鍧� + if (Object.prototype.toString.call(this.additionalModel) === "[object Array]") { + Modules.push(...this.additionalModel); + } else { + this.additionalModel && Modules.push(this.additionalModel); + } + + // 缈昏瘧妯″潡 + const TranslateModule = { + translate: ["value", customTranslate(this.translations || translationsCN)] + }; + Modules.push(TranslateModule); + + // 妯℃嫙娴佽浆妯″潡 + if (this.simulation) { + Modules.push(tokenSimulation); + } + + // 鏍规嵁闇�瑕佺殑娴佺▼绫诲瀷璁剧疆鎵╁睍鍏冪礌鏋勫缓妯″潡 + // if (this.prefix === "bpmn") { + // Modules.push(bpmnModdleExtension); + // } + if (this.prefix === "camunda") { + Modules.push(camundaModdleExtension); + } + if (this.prefix === "flowable") { + Modules.push(flowableModdleExtension); + } + if (this.prefix === "activiti") { + Modules.push(activitiModdleExtension); + } + + return Modules; + }, + moddleExtensions() { + const Extensions = {}; + // 浠呬娇鐢ㄧ敤鎴疯嚜瀹氫箟妯″潡 + if (this.onlyCustomizeModdle) { + return this.moddleExtension || null; + } + + // 鎻掑叆鐢ㄦ埛鑷畾涔夋ā鍧� + if (this.moddleExtension) { + for (let key in this.moddleExtension) { + Extensions[key] = this.moddleExtension[key]; + } + } + + // 鏍规嵁闇�瑕佺殑 "娴佺▼绫诲瀷" 璁剧疆 瀵瑰簲鐨勮В鏋愭枃浠� + if (this.prefix === "activiti") { + Extensions.activiti = activitiModdleDescriptor; + } + if (this.prefix === "flowable") { + Extensions.flowable = flowableModdleDescriptor; + } + if (this.prefix === "camunda") { + Extensions.camunda = camundaModdleDescriptor; + } + + return Extensions; + } + }, + mounted() { + this.initBpmnModeler(); + this.createNewDiagram(this.modelValue); + // this.$once("hook:beforeUnmount", () => { + // if (this.bpmnModeler) this.bpmnModeler.destroy(); + // this.$emit("destroy", this.bpmnModeler); + // this.bpmnModeler = null; + // }); + }, + beforeUnmount() { + if (this.bpmnModeler) this.bpmnModeler.destroy(); + this.$emit("destroy", this.bpmnModeler); + this.bpmnModeler = null; + }, + methods: { + onSave () { + return new Promise((resolve, reject) => { + if (this.bpmnModeler == null) { + reject(); + } + this.bpmnModeler.saveXML({ format: true }).then(({ xml }) => { + this.$emit('save', xml); + resolve(xml); + }); + }) + }, + initBpmnModeler() { + if (this.bpmnModeler) return; + this.bpmnModeler = new BpmnModeler({ + container: this.$refs["bpmn-canvas"], + keyboard: this.keyboard ? { bindTo: document } : null, + additionalModules: this.additionalModules, + moddleExtensions: this.moddleExtensions, + ...this.options + }); + this.$emit("init-finished", this.bpmnModeler); + this.initModelListeners(); + }, + initModelListeners() { + const EventBus = this.bpmnModeler.get("eventBus"); + const that = this; + // 娉ㄥ唽闇�瑕佺殑鐩戝惉浜嬩欢, 灏�. 鏇挎崲涓� - , 閬垮厤瑙f瀽寮傚父 + this.events.forEach(event => { + EventBus.on(event, function(eventObj) { + let eventName = event.replace(/\./g, "-"); + let element = eventObj ? eventObj.element : null; + that.$emit(eventName, element, eventObj); + }); + }); + // 鐩戝惉鍥惧舰鏀瑰彉杩斿洖xml + EventBus.on("commandStack.changed", async event => { + try { + this.recoverable = this.bpmnModeler.get("commandStack").canRedo(); + this.revocable = this.bpmnModeler.get("commandStack").canUndo(); + let { xml } = await this.bpmnModeler.saveXML({ format: true }); + this.$emit("commandStack-changed", event); + this.$emit('update:modelValue', xml); + this.$emit("change", xml); + } catch (e) { + console.error(`[Process Designer Warn]: ${e.message || e}`); + } + }); + // 鐩戝惉瑙嗗浘缂╂斁鍙樺寲 + this.bpmnModeler.on("canvas.viewbox.changed", ({ viewbox }) => { + this.$emit("canvas-viewbox-changed", { viewbox }); + const { scale } = viewbox; + this.defaultZoom = Math.floor(scale * 100) / 100; + }); + }, + /* 鍒涘缓鏂扮殑娴佺▼鍥� */ + async createNewDiagram(xml) { + // 灏嗗瓧绗︿覆杞崲鎴愬浘鏄剧ず鍑烘潵 + let newId = this.processId || `Process_${new Date().getTime()}`; + let newName = this.processName || `涓氬姟娴佺▼_${new Date().getTime()}`; + let xmlString = xml || DefaultEmptyXML(newId, newName, this.prefix); + try { + let { warnings } = await this.bpmnModeler.importXML(xmlString); + if (warnings && warnings.length) { + warnings.forEach(warn => console.warn(warn)); + } + } catch (e) { + console.error(`[Process Designer Warn]: ${e?.message || e}`); + } + }, + + // 涓嬭浇娴佺▼鍥惧埌鏈湴 + /** + * @param {string} type + * @param {*} name + */ + async downloadProcess(type, name) { + try { + const _this = this; + // 鎸夐渶瑕佺被鍨嬪垱寤烘枃浠跺苟涓嬭浇 + if (type === "xml" || type === "bpmn") { + const { err, xml } = await this.bpmnModeler.saveXML(); + // 璇诲彇寮傚父鏃舵姏鍑哄紓甯� + if (err) { + console.error(`[Process Designer Warn ]: ${err.message || err}`); + } + let { href, filename } = _this.setEncoded(type.toUpperCase(), name, xml); + downloadFunc(href, filename); + } else { + const { err, svg } = await this.bpmnModeler.saveSVG(); + // 璇诲彇寮傚父鏃舵姏鍑哄紓甯� + if (err) { + return console.error(err); + } + let { href, filename } = _this.setEncoded("SVG", name, svg); + downloadFunc(href, filename); + } + } catch (e) { + console.error(`[Process Designer Warn ]: ${e.message || e}`); + } + // 鏂囦欢涓嬭浇鏂规硶 + function downloadFunc(href, filename) { + if (href && filename) { + let a = document.createElement("a"); + a.download = filename; //鎸囧畾涓嬭浇鐨勬枃浠跺悕 + a.href = href; // URL瀵硅薄 + a.click(); // 妯℃嫙鐐瑰嚮 + URL.revokeObjectURL(a.href); // 閲婃斁URL 瀵硅薄 + } + } + }, + + // 鏍规嵁鎵�闇�绫诲瀷杩涜杞爜骞惰繑鍥炰笅杞藉湴鍧� + setEncoded(type, filename = "diagram", data) { + const encodedData = encodeURIComponent(data); + return { + filename: `${filename}.${type}`, + href: `data:application/${type === "svg" ? "text/xml" : "bpmn20-xml"};charset=UTF-8,${encodedData}`, + data: data + }; + }, + + // 鍔犺浇鏈湴鏂囦欢 + importLocalFile() { + const that = this; + const file = this.$refs.refFile.files[0]; + const reader = new FileReader(); + reader.readAsText(file); + reader.onload = function() { + let xmlStr = this.result; + that.createNewDiagram(xmlStr); + }; + }, + /* ------------------------------------------------ refs methods ------------------------------------------------------ */ + downloadProcessAsXml() { + this.downloadProcess("xml"); + }, + downloadProcessAsBpmn() { + this.downloadProcess("bpmn"); + }, + downloadProcessAsSvg() { + this.downloadProcess("svg"); + }, + processSimulation() { + this.simulationStatus = !this.simulationStatus; + this.simulation && this.bpmnModeler.get("toggleMode").toggleMode(); + }, + processRedo() { + this.bpmnModeler.get("commandStack").redo(); + }, + processUndo() { + this.bpmnModeler.get("commandStack").undo(); + }, + processZoomIn(zoomStep = 0.1) { + let newZoom = Math.floor(this.defaultZoom * 100 + zoomStep * 100) / 100; + if (newZoom > 4) { + throw new Error("[Process Designer Warn ]: The zoom ratio cannot be greater than 4"); + } + this.defaultZoom = newZoom; + this.bpmnModeler.get("canvas").zoom(this.defaultZoom); + }, + processZoomOut(zoomStep = 0.1) { + let newZoom = Math.floor(this.defaultZoom * 100 - zoomStep * 100) / 100; + if (newZoom < 0.2) { + throw new Error("[Process Designer Warn ]: The zoom ratio cannot be less than 0.2"); + } + this.defaultZoom = newZoom; + this.bpmnModeler.get("canvas").zoom(this.defaultZoom); + }, + processZoomTo(newZoom = 1) { + if (newZoom < 0.2) { + throw new Error("[Process Designer Warn ]: The zoom ratio cannot be less than 0.2"); + } + if (newZoom > 4) { + throw new Error("[Process Designer Warn ]: The zoom ratio cannot be greater than 4"); + } + this.defaultZoom = newZoom; + this.bpmnModeler.get("canvas").zoom(newZoom); + }, + processReZoom() { + this.defaultZoom = 1; + this.bpmnModeler.get("canvas").zoom("fit-viewport", "auto"); + }, + processRestart() { + this.recoverable = false; + this.revocable = false; + this.createNewDiagram(null); + }, + elementsAlign(align) { + const Align = this.bpmnModeler.get("alignElements"); + const Selection = this.bpmnModeler.get("selection"); + const SelectedElements = Selection.get(); + if (!SelectedElements || SelectedElements.length <= 1) { + this.$message.warning("璇锋寜浣� Ctrl 閿�夋嫨澶氫釜鍏冪礌瀵归綈"); + return; + } + this.$confirm("鑷姩瀵归綈鍙兘閫犳垚鍥惧舰鍙樺舰锛屾槸鍚︾户缁紵", "璀﹀憡", { + confirmButtonText: "纭畾", + cancelButtonText: "鍙栨秷", + type: "warning" + }).then(() => Align.trigger(SelectedElements, align)); + }, + /*----------------------------- 鏂规硶缁撴潫 ---------------------------------*/ + previewProcessXML() { + this.bpmnModeler.saveXML({ format: true }).then(({ xml }) => { + this.previewResult = xml; + this.previewType = 'xml'; + this.cmOptions.mode = 'xml' + this.previewModelVisible = true; + }); + }, + previewProcessJson() { + // this.bpmnModeler.saveXML({ format: true }).then(({ xml }) => { + // this.previewResult = convert.xml2json(xml, { spaces: 2 }); + // this.previewType = "json"; + // this.previewModelVisible = true; + // }); + const newConvert = new X2JS(); + this.bpmnModeler.saveXML({ format: true }).then(({ xml }) => { + const { definitions } = newConvert.xml2js(xml); + if (definitions) { + this.previewResult = JSON.stringify(definitions, null, 4); + } else { + this.previewResult = ""; + } + + this.previewType = "json"; + this.previewModelVisible = true; + }); + } + } +}; +</script> diff --git a/ruoyi-ui/apps/web-antd/src/package/designer/index.js b/ruoyi-ui/apps/web-antd/src/package/designer/index.js new file mode 100644 index 0000000..333d1bc --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/designer/index.js @@ -0,0 +1,7 @@ +import MyProcessDesigner from './ProcessDesigner.vue'; + +MyProcessDesigner.install = function(Vue) { + Vue.component(MyProcessDesigner.name, MyProcessDesigner); +}; + +export default MyProcessDesigner; diff --git a/ruoyi-ui/apps/web-antd/src/package/designer/plugins/content-pad/contentPadProvider.js b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/content-pad/contentPadProvider.js new file mode 100644 index 0000000..d3eae19 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/content-pad/contentPadProvider.js @@ -0,0 +1,390 @@ +import { assign, forEach, isArray } from 'min-dash'; + +import { is } from 'bpmn-js/lib/util/ModelUtil'; + +import { isExpanded, isEventSubProcess } from 'bpmn-js/lib/util/DiUtil'; + +import { isAny } from 'bpmn-js/lib/features/modeling/util/ModelingUtil'; + +import { getChildLanes } from 'bpmn-js/lib/features/modeling/util/LaneUtil'; + +import { hasPrimaryModifier } from 'diagram-js/lib/util/Mouse'; + +/** + * A provider for BPMN 2.0 elements context pad + */ +export default function ContextPadProvider( + config, + injector, + eventBus, + contextPad, + modeling, + elementFactory, + connect, + create, + popupMenu, + canvas, + rules, + translate, + elementRegistry +) { + config = config || {}; + + contextPad.registerProvider(this); + + this._contextPad = contextPad; + + this._modeling = modeling; + + this._elementFactory = elementFactory; + this._connect = connect; + this._create = create; + this._popupMenu = popupMenu; + this._canvas = canvas; + this._rules = rules; + this._translate = translate; + + if (config.autoPlace !== false) { + this._autoPlace = injector.get('autoPlace', false); + } + + eventBus.on('create.end', 250, function(event) { + var context = event.context; + var shape = context.shape; + + if (!hasPrimaryModifier(event) || !contextPad.isOpen(shape)) { + return; + } + + var entries = contextPad.getEntries(shape); + + if (entries.replace) { + entries.replace.action.click(event, shape); + } + }); +} + +ContextPadProvider.$inject = [ + 'config.contextPad', + 'injector', + 'eventBus', + 'contextPad', + 'modeling', + 'elementFactory', + 'connect', + 'create', + 'popupMenu', + 'canvas', + 'rules', + 'translate', + 'elementRegistry' +]; + +ContextPadProvider.prototype.getContextPadEntries = function(element) { + var contextPad = this._contextPad; + var modeling = this._modeling; + var elementFactory = this._elementFactory; + var connect = this._connect; + var create = this._create; + var popupMenu = this._popupMenu; + var canvas = this._canvas; + var rules = this._rules; + var autoPlace = this._autoPlace; + var translate = this._translate; + + var actions = {}; + + if (element.type === 'label') { + return actions; + } + + var businessObject = element.businessObject; + + function startConnect(event, element) { + connect.start(event, element); + } + + function removeElement() { + modeling.removeElements([element]); + } + + function getReplaceMenuPosition(element) { + var Y_OFFSET = 5; + + var diagramContainer = canvas.getContainer(); + var pad = contextPad.getPad(element).html; + + var diagramRect = diagramContainer.getBoundingClientRect(); + var padRect = pad.getBoundingClientRect(); + + var top = padRect.top - diagramRect.top; + var left = padRect.left - diagramRect.left; + + var pos = { + x: left, + y: top + padRect.height + Y_OFFSET + }; + + return pos; + } + + /** + * Create an append action + * + * @param {string} type + * @param {string} className + * @param {string} [title] + * @param {Object} [options] + * + * @return {Object} descriptor + */ + function appendAction(type, className, title, options) { + if (typeof title !== 'string') { + options = title; + title = translate('Append {type}', { type: type.replace(/^bpmn:/, '') }); + } + + function appendStart(event, element) { + var shape = elementFactory.createShape(assign({ type: type }, options)); + create.start(event, shape, { + source: element + }); + } + + var append = autoPlace + ? function(event, element) { + var shape = elementFactory.createShape(assign({ type: type }, options)); + + autoPlace.append(element, shape); + } + : appendStart; + + return { + group: 'model', + className: className, + title: title, + action: { + dragstart: appendStart, + click: append + } + }; + } + + function splitLaneHandler(count) { + return function(event, element) { + // actual split + modeling.splitLane(element, count); + + // refresh context pad after split to + // get rid of split icons + contextPad.open(element, true); + }; + } + + if (isAny(businessObject, ['bpmn:Lane', 'bpmn:Participant']) && isExpanded(businessObject)) { + var childLanes = getChildLanes(element); + + assign(actions, { + 'lane-insert-above': { + group: 'lane-insert-above', + className: 'bpmn-icon-lane-insert-above', + title: translate('Add Lane above'), + action: { + click: function(event, element) { + modeling.addLane(element, 'top'); + } + } + } + }); + + if (childLanes.length < 2) { + if (element.height >= 120) { + assign(actions, { + 'lane-divide-two': { + group: 'lane-divide', + className: 'bpmn-icon-lane-divide-two', + title: translate('Divide into two Lanes'), + action: { + click: splitLaneHandler(2) + } + } + }); + } + + if (element.height >= 180) { + assign(actions, { + 'lane-divide-three': { + group: 'lane-divide', + className: 'bpmn-icon-lane-divide-three', + title: translate('Divide into three Lanes'), + action: { + click: splitLaneHandler(3) + } + } + }); + } + } + + assign(actions, { + 'lane-insert-below': { + group: 'lane-insert-below', + className: 'bpmn-icon-lane-insert-below', + title: translate('Add Lane below'), + action: { + click: function(event, element) { + modeling.addLane(element, 'bottom'); + } + } + } + }); + } + + if (is(businessObject, 'bpmn:FlowNode')) { + if (is(businessObject, 'bpmn:EventBasedGateway')) { + assign(actions, { + 'append.receive-task': appendAction('bpmn:ReceiveTask', 'bpmn-icon-receive-task', translate('Append ReceiveTask')), + 'append.message-intermediate-event': appendAction( + 'bpmn:IntermediateCatchEvent', + 'bpmn-icon-intermediate-event-catch-message', + translate('Append MessageIntermediateCatchEvent'), + { eventDefinitionType: 'bpmn:MessageEventDefinition' } + ), + 'append.timer-intermediate-event': appendAction( + 'bpmn:IntermediateCatchEvent', + 'bpmn-icon-intermediate-event-catch-timer', + translate('Append TimerIntermediateCatchEvent'), + { eventDefinitionType: 'bpmn:TimerEventDefinition' } + ), + 'append.condition-intermediate-event': appendAction( + 'bpmn:IntermediateCatchEvent', + 'bpmn-icon-intermediate-event-catch-condition', + translate('Append ConditionIntermediateCatchEvent'), + { eventDefinitionType: 'bpmn:ConditionalEventDefinition' } + ), + 'append.signal-intermediate-event': appendAction( + 'bpmn:IntermediateCatchEvent', + 'bpmn-icon-intermediate-event-catch-signal', + translate('Append SignalIntermediateCatchEvent'), + { eventDefinitionType: 'bpmn:SignalEventDefinition' } + ) + }); + } else if (isEventType(businessObject, 'bpmn:BoundaryEvent', 'bpmn:CompensateEventDefinition')) { + assign(actions, { + 'append.compensation-activity': appendAction('bpmn:Task', 'bpmn-icon-task', translate('Append compensation activity'), { + isForCompensation: true + }) + }); + } else if ( + !is(businessObject, 'bpmn:EndEvent') && + !businessObject.isForCompensation && + !isEventType(businessObject, 'bpmn:IntermediateThrowEvent', 'bpmn:LinkEventDefinition') && + !isEventSubProcess(businessObject) + ) { + assign(actions, { + 'append.end-event': appendAction('bpmn:EndEvent', 'bpmn-icon-end-event-none', translate('Append EndEvent')), + 'append.gateway': appendAction('bpmn:ExclusiveGateway', 'bpmn-icon-gateway-none', translate('Append Gateway')), + 'append.append-task': appendAction('bpmn:UserTask', 'bpmn-icon-user-task', translate('Append Task')), + 'append.intermediate-event': appendAction( + 'bpmn:IntermediateThrowEvent', + 'bpmn-icon-intermediate-event-none', + translate('Append Intermediate/Boundary Event') + ) + }); + } + } + + if (!popupMenu.isEmpty(element, 'bpmn-replace')) { + // Replace menu entry + assign(actions, { + replace: { + group: 'edit', + className: 'bpmn-icon-screw-wrench', + title: translate('Change type'), + action: { + click: function(event, element) { + var position = assign(getReplaceMenuPosition(element), { + cursor: { x: event.x, y: event.y } + }); + + popupMenu.open(element, 'bpmn-replace', position); + } + } + } + }); + } + + if (isAny(businessObject, ['bpmn:FlowNode', 'bpmn:InteractionNode', 'bpmn:DataObjectReference', 'bpmn:DataStoreReference'])) { + assign(actions, { + 'append.text-annotation': appendAction('bpmn:TextAnnotation', 'bpmn-icon-text-annotation'), + + connect: { + group: 'connect', + className: 'bpmn-icon-connection-multi', + title: translate('Connect using ' + (businessObject.isForCompensation ? '' : 'Sequence/MessageFlow or ') + 'Association'), + action: { + click: startConnect, + dragstart: startConnect + } + } + }); + } + + if (isAny(businessObject, ['bpmn:DataObjectReference', 'bpmn:DataStoreReference'])) { + assign(actions, { + connect: { + group: 'connect', + className: 'bpmn-icon-connection-multi', + title: translate('Connect using DataInputAssociation'), + action: { + click: startConnect, + dragstart: startConnect + } + } + }); + } + + if (is(businessObject, 'bpmn:Group')) { + assign(actions, { + 'append.text-annotation': appendAction('bpmn:TextAnnotation', 'bpmn-icon-text-annotation') + }); + } + + // delete element entry, only show if allowed by rules + var deleteAllowed = rules.allowed('elements.delete', { elements: [element] }); + + if (isArray(deleteAllowed)) { + // was the element returned as a deletion candidate? + deleteAllowed = deleteAllowed[0] === element; + } + + if (deleteAllowed) { + assign(actions, { + delete: { + group: 'edit', + className: 'bpmn-icon-trash', + title: translate('Remove'), + action: { + click: removeElement + } + } + }); + } + + return actions; +}; + +// helpers ///////// + +function isEventType(eventBo, type, definition) { + var isType = eventBo.$instanceOf(type); + var isDefinition = false; + + var definitions = eventBo.eventDefinitions || []; + forEach(definitions, function(def) { + if (def.$type === definition) { + isDefinition = true; + } + }); + + return isType && isDefinition; +} diff --git a/ruoyi-ui/apps/web-antd/src/package/designer/plugins/content-pad/index.js b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/content-pad/index.js new file mode 100644 index 0000000..ad826f1 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/content-pad/index.js @@ -0,0 +1,6 @@ +import CustomContextPadProvider from './contentPadProvider'; + +export default { + __init__: ['contextPadProvider'], + contextPadProvider: ['type', CustomContextPadProvider] +}; diff --git a/ruoyi-ui/apps/web-antd/src/package/designer/plugins/defaultEmpty.js b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/defaultEmpty.js new file mode 100644 index 0000000..46dd4c1 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/defaultEmpty.js @@ -0,0 +1,24 @@ +export default (key, name, type) => { + if (!type) type = 'camunda'; + const TYPE_TARGET = { + activiti: 'http://activiti.org/bpmn', + camunda: 'http://bpmn.io/schema/bpmn', + flowable: 'http://flowable.org/bpmn' + }; + return `<?xml version="1.0" encoding="UTF-8"?> +<bpmn2:definitions + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL" + xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" + xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" + xmlns:di="http://www.omg.org/spec/DD/20100524/DI" + id="diagram_${key}" + targetNamespace="${TYPE_TARGET[type]}"> + <bpmn2:process id="${key}" name="${name}" isExecutable="true"> + </bpmn2:process> + <bpmndi:BPMNDiagram id="BPMNDiagram_1"> + <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="${key}"> + </bpmndi:BPMNPlane> + </bpmndi:BPMNDiagram> +</bpmn2:definitions>`; +}; diff --git a/ruoyi-ui/apps/web-antd/src/package/designer/plugins/descriptor/activitiDescriptor.json b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/descriptor/activitiDescriptor.json new file mode 100644 index 0000000..9daed50 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/descriptor/activitiDescriptor.json @@ -0,0 +1,1071 @@ +{ + "name": "Activiti", + "uri": "http://activiti.org/bpmn", + "prefix": "activiti", + "xml": { + "tagAlias": "lowerCase" + }, + "associations": [], + "types": [ + { + "name": "Definitions", + "isAbstract": true, + "extends": [ + "bpmn:Definitions" + ], + "properties": [ + { + "name": "diagramRelationId", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "InOutBinding", + "superClass": [ + "Element" + ], + "isAbstract": true, + "properties": [ + { + "name": "source", + "isAttr": true, + "type": "String" + }, + { + "name": "sourceExpression", + "isAttr": true, + "type": "String" + }, + { + "name": "target", + "isAttr": true, + "type": "String" + }, + { + "name": "businessKey", + "isAttr": true, + "type": "String" + }, + { + "name": "local", + "isAttr": true, + "type": "Boolean", + "default": false + }, + { + "name": "variables", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "In", + "superClass": [ + "InOutBinding" + ], + "meta": { + "allowedIn": [ + "bpmn:CallActivity" + ] + } + }, + { + "name": "Out", + "superClass": [ + "InOutBinding" + ], + "meta": { + "allowedIn": [ + "bpmn:CallActivity" + ] + } + }, + { + "name": "AsyncCapable", + "isAbstract": true, + "extends": [ + "bpmn:Activity", + "bpmn:Gateway", + "bpmn:Event" + ], + "properties": [ + { + "name": "async", + "isAttr": true, + "type": "Boolean", + "default": false + }, + { + "name": "asyncBefore", + "isAttr": true, + "type": "Boolean", + "default": false + }, + { + "name": "asyncAfter", + "isAttr": true, + "type": "Boolean", + "default": false + }, + { + "name": "exclusive", + "isAttr": true, + "type": "Boolean", + "default": true + } + ] + }, + { + "name": "JobPriorized", + "isAbstract": true, + "extends": [ + "bpmn:Process", + "activiti:AsyncCapable" + ], + "properties": [ + { + "name": "jobPriority", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "SignalEventDefinition", + "isAbstract": true, + "extends": [ + "bpmn:SignalEventDefinition" + ], + "properties": [ + { + "name": "async", + "isAttr": true, + "type": "Boolean", + "default": false + } + ] + }, + { + "name": "ErrorEventDefinition", + "isAbstract": true, + "extends": [ + "bpmn:ErrorEventDefinition" + ], + "properties": [ + { + "name": "errorCodeVariable", + "isAttr": true, + "type": "String" + }, + { + "name": "errorMessageVariable", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "Error", + "isAbstract": true, + "extends": [ + "bpmn:Error" + ], + "properties": [ + { + "name": "activiti:errorMessage", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "PotentialStarter", + "superClass": [ + "Element" + ], + "properties": [ + { + "name": "resourceAssignmentExpression", + "type": "bpmn:ResourceAssignmentExpression" + } + ] + }, + { + "name": "FormSupported", + "isAbstract": true, + "extends": [ + "bpmn:StartEvent", + "bpmn:UserTask" + ], + "properties": [ + { + "name": "formHandlerClass", + "isAttr": true, + "type": "String" + }, + { + "name": "formKey", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "TemplateSupported", + "isAbstract": true, + "extends": [ + "bpmn:Process", + "bpmn:FlowElement" + ], + "properties": [ + { + "name": "modelerTemplate", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "Initiator", + "isAbstract": true, + "extends": [ "bpmn:StartEvent" ], + "properties": [ + { + "name": "initiator", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "ScriptTask", + "isAbstract": true, + "extends": [ + "bpmn:ScriptTask" + ], + "properties": [ + { + "name": "resultVariable", + "isAttr": true, + "type": "String" + }, + { + "name": "resource", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "Process", + "isAbstract": true, + "extends": [ + "bpmn:Process" + ], + "properties": [ + { + "name": "candidateStarterGroups", + "isAttr": true, + "type": "String" + }, + { + "name": "candidateStarterUsers", + "isAttr": true, + "type": "String" + }, + { + "name": "versionTag", + "isAttr": true, + "type": "String" + }, + { + "name": "historyTimeToLive", + "isAttr": true, + "type": "String" + }, + { + "name": "isStartableInTasklist", + "isAttr": true, + "type": "Boolean", + "default": true + }, + { + "name":"executionListener", + "isAbstract": true, + "type":"Expression" + } + ] + }, + { + "name": "EscalationEventDefinition", + "isAbstract": true, + "extends": [ + "bpmn:EscalationEventDefinition" + ], + "properties": [ + { + "name": "escalationCodeVariable", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "FormalExpression", + "isAbstract": true, + "extends": [ + "bpmn:FormalExpression" + ], + "properties": [ + { + "name": "resource", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "multiinstance_type", + "superClass":[ + "Element" + ] + }, + { + "name": "multiinstance_condition", + "superClass":[ + "Element" + ] + }, + { + "name": "Assignable", + "extends": [ "bpmn:UserTask" ], + "properties": [ + { + "name": "assignee", + "isAttr": true, + "type": "String" + }, + { + "name": "candidateUsers", + "isAttr": true, + "type": "String" + }, + { + "name": "candidateGroups", + "isAttr": true, + "type": "String" + }, + { + "name": "dueDate", + "isAttr": true, + "type": "String" + }, + { + "name": "followUpDate", + "isAttr": true, + "type": "String" + }, + { + "name": "priority", + "isAttr": true, + "type": "String" + }, + { + "name": "multiinstance_condition", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "CallActivity", + "extends": [ "bpmn:CallActivity" ], + "properties": [ + { + "name": "calledElementBinding", + "isAttr": true, + "type": "String", + "default": "latest" + }, + { + "name": "calledElementVersion", + "isAttr": true, + "type": "String" + }, + { + "name": "calledElementVersionTag", + "isAttr": true, + "type": "String" + }, + { + "name": "calledElementTenantId", + "isAttr": true, + "type": "String" + }, + { + "name": "caseRef", + "isAttr": true, + "type": "String" + }, + { + "name": "caseBinding", + "isAttr": true, + "type": "String", + "default": "latest" + }, + { + "name": "caseVersion", + "isAttr": true, + "type": "String" + }, + { + "name": "caseTenantId", + "isAttr": true, + "type": "String" + }, + { + "name": "variableMappingClass", + "isAttr": true, + "type": "String" + }, + { + "name": "variableMappingDelegateExpression", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "ServiceTaskLike", + "extends": [ + "bpmn:ServiceTask", + "bpmn:BusinessRuleTask", + "bpmn:SendTask", + "bpmn:MessageEventDefinition" + ], + "properties": [ + { + "name": "expression", + "isAttr": true, + "type": "String" + }, + { + "name": "class", + "isAttr": true, + "type": "String" + }, + { + "name": "delegateExpression", + "isAttr": true, + "type": "String" + }, + { + "name": "resultVariable", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "DmnCapable", + "extends": [ + "bpmn:BusinessRuleTask" + ], + "properties": [ + { + "name": "decisionRef", + "isAttr": true, + "type": "String" + }, + { + "name": "decisionRefBinding", + "isAttr": true, + "type": "String", + "default": "latest" + }, + { + "name": "decisionRefVersion", + "isAttr": true, + "type": "String" + }, + { + "name": "mapDecisionResult", + "isAttr": true, + "type": "String", + "default": "resultList" + }, + { + "name": "decisionRefTenantId", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "ExternalCapable", + "extends": [ + "activiti:ServiceTaskLike" + ], + "properties": [ + { + "name": "type", + "isAttr": true, + "type": "String" + }, + { + "name": "topic", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "TaskPriorized", + "extends": [ + "bpmn:Process", + "activiti:ExternalCapable" + ], + "properties": [ + { + "name": "taskPriority", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "Properties", + "superClass": [ + "Element" + ], + "meta": { + "allowedIn": [ "*" ] + }, + "properties": [ + { + "name": "values", + "type": "Property", + "isMany": true + } + ] + }, + { + "name": "Property", + "superClass": [ + "Element" + ], + "properties": [ + { + "name": "id", + "type": "String", + "isAttr": true + }, + { + "name": "name", + "type": "String", + "isAttr": true + }, + { + "name": "value", + "type": "String", + "isAttr": true + } + ] + }, + { + "name": "Connector", + "superClass": [ + "Element" + ], + "meta": { + "allowedIn": [ + "activiti:ServiceTaskLike" + ] + }, + "properties": [ + { + "name": "inputOutput", + "type": "InputOutput" + }, + { + "name": "connectorId", + "type": "String" + } + ] + }, + { + "name": "InputOutput", + "superClass": [ + "Element" + ], + "meta": { + "allowedIn": [ + "bpmn:FlowNode", + "activiti:Connector" + ] + }, + "properties": [ + { + "name": "inputOutput", + "type": "InputOutput" + }, + { + "name": "connectorId", + "type": "String" + }, + { + "name": "inputParameters", + "isMany": true, + "type": "InputParameter" + }, + { + "name": "outputParameters", + "isMany": true, + "type": "OutputParameter" + } + ] + }, + { + "name": "InputOutputParameter", + "properties": [ + { + "name": "name", + "isAttr": true, + "type": "String" + }, + { + "name": "value", + "isBody": true, + "type": "String" + }, + { + "name": "definition", + "type": "InputOutputParameterDefinition" + } + ] + }, + { + "name": "InputOutputParameterDefinition", + "isAbstract": true + }, + { + "name": "List", + "superClass": [ "InputOutputParameterDefinition" ], + "properties": [ + { + "name": "items", + "isMany": true, + "type": "InputOutputParameterDefinition" + } + ] + }, + { + "name": "Map", + "superClass": [ "InputOutputParameterDefinition" ], + "properties": [ + { + "name": "entries", + "isMany": true, + "type": "Entry" + } + ] + }, + { + "name": "Entry", + "properties": [ + { + "name": "key", + "isAttr": true, + "type": "String" + }, + { + "name": "value", + "isBody": true, + "type": "String" + }, + { + "name": "definition", + "type": "InputOutputParameterDefinition" + } + ] + }, + { + "name": "Value", + "superClass": [ + "InputOutputParameterDefinition" + ], + "properties": [ + { + "name": "id", + "isAttr": true, + "type": "String" + }, + { + "name": "name", + "isAttr": true, + "type": "String" + }, + { + "name": "value", + "isBody": true, + "type": "String" + } + ] + }, + { + "name": "Script", + "superClass": [ "InputOutputParameterDefinition" ], + "properties": [ + { + "name": "scriptFormat", + "isAttr": true, + "type": "String" + }, + { + "name": "resource", + "isAttr": true, + "type": "String" + }, + { + "name": "value", + "isBody": true, + "type": "String" + } + ] + }, + { + "name": "Field", + "superClass": [ "Element" ], + "meta": { + "allowedIn": [ + "activiti:ServiceTaskLike", + "activiti:ExecutionListener", + "activiti:TaskListener" + ] + }, + "properties": [ + { + "name": "name", + "isAttr": true, + "type": "String" + }, + { + "name": "expression", + "type": "String" + }, + { + "name": "stringValue", + "isAttr": true, + "type": "String" + }, + { + "name": "string", + "type": "String" + } + ] + }, + { + "name": "InputParameter", + "superClass": [ "InputOutputParameter" ] + }, + { + "name": "OutputParameter", + "superClass": [ "InputOutputParameter" ] + }, + { + "name": "Collectable", + "isAbstract": true, + "extends": [ "bpmn:MultiInstanceLoopCharacteristics" ], + "superClass": [ "activiti:AsyncCapable" ], + "properties": [ + { + "name": "collection", + "isAttr": true, + "type": "String" + }, + { + "name": "elementVariable", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "FailedJobRetryTimeCycle", + "superClass": [ "Element" ], + "meta": { + "allowedIn": [ + "activiti:AsyncCapable", + "bpmn:MultiInstanceLoopCharacteristics" + ] + }, + "properties": [ + { + "name": "body", + "isBody": true, + "type": "String" + } + ] + }, + { + "name": "ExecutionListener", + "superClass": [ "Element" ], + "meta": { + "allowedIn": [ + "bpmn:Task", + "bpmn:ServiceTask", + "bpmn:UserTask", + "bpmn:BusinessRuleTask", + "bpmn:ScriptTask", + "bpmn:ReceiveTask", + "bpmn:ManualTask", + "bpmn:ExclusiveGateway", + "bpmn:SequenceFlow", + "bpmn:ParallelGateway", + "bpmn:InclusiveGateway", + "bpmn:EventBasedGateway", + "bpmn:StartEvent", + "bpmn:IntermediateCatchEvent", + "bpmn:IntermediateThrowEvent", + "bpmn:EndEvent", + "bpmn:BoundaryEvent", + "bpmn:CallActivity", + "bpmn:SubProcess", + "bpmn:Process" + ] + }, + "properties": [ + { + "name": "expression", + "isAttr": true, + "type": "String" + }, + { + "name": "class", + "isAttr": true, + "type": "String" + }, + { + "name": "delegateExpression", + "isAttr": true, + "type": "String" + }, + { + "name": "event", + "isAttr": true, + "type": "String" + }, + { + "name": "script", + "type": "Script" + }, + { + "name": "fields", + "type": "Field", + "isMany": true + } + ] + }, + { + "name": "TaskListener", + "superClass": [ "Element" ], + "meta": { + "allowedIn": [ + "bpmn:UserTask" + ] + }, + "properties": [ + { + "name": "expression", + "isAttr": true, + "type": "String" + }, + { + "name": "class", + "isAttr": true, + "type": "String" + }, + { + "name": "delegateExpression", + "isAttr": true, + "type": "String" + }, + { + "name": "event", + "isAttr": true, + "type": "String" + }, + { + "name": "script", + "type": "Script" + }, + { + "name": "fields", + "type": "Field", + "isMany": true + } + ] + }, + { + "name": "FormProperty", + "superClass": [ "Element" ], + "meta": { + "allowedIn": [ + "bpmn:StartEvent", + "bpmn:UserTask" + ] + }, + "properties": [ + { + "name": "id", + "type": "String", + "isAttr": true + }, + { + "name": "name", + "type": "String", + "isAttr": true + }, + { + "name": "type", + "type": "String", + "isAttr": true + }, + { + "name": "required", + "type": "String", + "isAttr": true + }, + { + "name": "readable", + "type": "String", + "isAttr": true + }, + { + "name": "writable", + "type": "String", + "isAttr": true + }, + { + "name": "variable", + "type": "String", + "isAttr": true + }, + { + "name": "expression", + "type": "String", + "isAttr": true + }, + { + "name": "datePattern", + "type": "String", + "isAttr": true + }, + { + "name": "default", + "type": "String", + "isAttr": true + }, + { + "name": "values", + "type": "Value", + "isMany": true + } + ] + }, + { + "name": "FormProperty", + "superClass": [ "Element" ], + "properties": [ + { + "name": "id", + "type": "String", + "isAttr": true + }, + { + "name": "label", + "type": "String", + "isAttr": true + }, + { + "name": "type", + "type": "String", + "isAttr": true + }, + { + "name": "datePattern", + "type": "String", + "isAttr": true + }, + { + "name": "defaultValue", + "type": "String", + "isAttr": true + }, + { + "name": "properties", + "type": "Properties" + }, + { + "name": "validation", + "type": "Validation" + }, + { + "name": "values", + "type": "Value", + "isMany": true + } + ] + }, + { + "name": "Validation", + "superClass": [ "Element" ], + "properties": [ + { + "name": "constraints", + "type": "Constraint", + "isMany": true + } + ] + }, + { + "name": "Constraint", + "superClass": [ "Element" ], + "properties": [ + { + "name": "name", + "type": "String", + "isAttr": true + }, + { + "name": "config", + "type": "String", + "isAttr": true + } + ] + }, + { + "name": "ConditionalEventDefinition", + "isAbstract": true, + "extends": [ + "bpmn:ConditionalEventDefinition" + ], + "properties": [ + { + "name": "variableName", + "isAttr": true, + "type": "String" + }, + { + "name": "variableEvent", + "isAttr": true, + "type": "String" + } + ] + } + ], + "emumerations": [ ] +} diff --git a/ruoyi-ui/apps/web-antd/src/package/designer/plugins/descriptor/camundaDescriptor.json b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/descriptor/camundaDescriptor.json new file mode 100644 index 0000000..a57dbe6 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/descriptor/camundaDescriptor.json @@ -0,0 +1,1087 @@ +{ + "name": "Camunda", + "uri": "http://camunda.org/schema/1.0/bpmn", + "prefix": "camunda", + "xml": { + "tagAlias": "lowerCase" + }, + "associations": [], + "types": [ + { + "name": "Definitions", + "isAbstract": true, + "extends": [ + "bpmn:Definitions" + ], + "properties": [ + { + "name": "diagramRelationId", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "InOutBinding", + "superClass": [ + "Element" + ], + "isAbstract": true, + "properties": [ + { + "name": "source", + "isAttr": true, + "type": "String" + }, + { + "name": "sourceExpression", + "isAttr": true, + "type": "String" + }, + { + "name": "target", + "isAttr": true, + "type": "String" + }, + { + "name": "businessKey", + "isAttr": true, + "type": "String" + }, + { + "name": "local", + "isAttr": true, + "type": "Boolean", + "default": false + }, + { + "name": "variables", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "In", + "superClass": [ + "InOutBinding" + ], + "meta": { + "allowedIn": [ + "bpmn:CallActivity", + "bpmn:SignalEventDefinition" + ] + } + }, + { + "name": "Out", + "superClass": [ + "InOutBinding" + ], + "meta": { + "allowedIn": [ + "bpmn:CallActivity" + ] + } + }, + { + "name": "AsyncCapable", + "isAbstract": true, + "extends": [ + "bpmn:Activity", + "bpmn:Gateway", + "bpmn:Event" + ], + "properties": [ + { + "name": "async", + "isAttr": true, + "type": "Boolean", + "default": false + }, + { + "name": "asyncBefore", + "isAttr": true, + "type": "Boolean", + "default": false + }, + { + "name": "asyncAfter", + "isAttr": true, + "type": "Boolean", + "default": false + }, + { + "name": "exclusive", + "isAttr": true, + "type": "Boolean", + "default": true + } + ] + }, + { + "name": "JobPriorized", + "isAbstract": true, + "extends": [ + "bpmn:Process", + "camunda:AsyncCapable" + ], + "properties": [ + { + "name": "jobPriority", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "SignalEventDefinition", + "isAbstract": true, + "extends": [ + "bpmn:SignalEventDefinition" + ], + "properties": [ + { + "name": "async", + "isAttr": true, + "type": "Boolean", + "default": false + } + ] + }, + { + "name": "ErrorEventDefinition", + "isAbstract": true, + "extends": [ + "bpmn:ErrorEventDefinition" + ], + "properties": [ + { + "name": "errorCodeVariable", + "isAttr": true, + "type": "String" + }, + { + "name": "errorMessageVariable", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "Error", + "isAbstract": true, + "extends": [ + "bpmn:Error" + ], + "properties": [ + { + "name": "camunda:errorMessage", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "PotentialStarter", + "superClass": [ + "Element" + ], + "properties": [ + { + "name": "resourceAssignmentExpression", + "type": "bpmn:ResourceAssignmentExpression" + } + ] + }, + { + "name": "FormSupported", + "isAbstract": true, + "extends": [ + "bpmn:StartEvent", + "bpmn:UserTask" + ], + "properties": [ + { + "name": "formHandlerClass", + "isAttr": true, + "type": "String" + }, + { + "name": "formKey", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "TemplateSupported", + "isAbstract": true, + "extends": [ + "bpmn:Process", + "bpmn:FlowElement" + ], + "properties": [ + { + "name": "modelerTemplate", + "isAttr": true, + "type": "String" + }, + { + "name": "modelerTemplateVersion", + "isAttr": true, + "type": "Integer" + } + ] + }, + { + "name": "Initiator", + "isAbstract": true, + "extends": [ "bpmn:StartEvent" ], + "properties": [ + { + "name": "initiator", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "ScriptTask", + "isAbstract": true, + "extends": [ + "bpmn:ScriptTask" + ], + "properties": [ + { + "name": "resultVariable", + "isAttr": true, + "type": "String" + }, + { + "name": "resource", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "Process", + "isAbstract": true, + "extends": [ + "bpmn:Process" + ], + "properties": [ + { + "name": "candidateStarterGroups", + "isAttr": true, + "type": "String" + }, + { + "name": "candidateStarterUsers", + "isAttr": true, + "type": "String" + }, + { + "name": "versionTag", + "isAttr": true, + "type": "String" + }, + { + "name": "historyTimeToLive", + "isAttr": true, + "type": "String" + }, + { + "name": "isStartableInTasklist", + "isAttr": true, + "type": "Boolean", + "default": true + } + ] + }, + { + "name": "EscalationEventDefinition", + "isAbstract": true, + "extends": [ + "bpmn:EscalationEventDefinition" + ], + "properties": [ + { + "name": "escalationCodeVariable", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "FormalExpression", + "isAbstract": true, + "extends": [ + "bpmn:FormalExpression" + ], + "properties": [ + { + "name": "resource", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "Assignable", + "extends": [ "bpmn:UserTask" ], + "properties": [ + { + "name": "assignee", + "isAttr": true, + "type": "String" + }, + { + "name": "candidateUsers", + "isAttr": true, + "type": "String" + }, + { + "name": "candidateGroups", + "isAttr": true, + "type": "String" + }, + { + "name": "dueDate", + "isAttr": true, + "type": "String" + }, + { + "name": "followUpDate", + "isAttr": true, + "type": "String" + }, + { + "name": "priority", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "CallActivity", + "extends": [ "bpmn:CallActivity" ], + "properties": [ + { + "name": "calledElementBinding", + "isAttr": true, + "type": "String", + "default": "latest" + }, + { + "name": "calledElementVersion", + "isAttr": true, + "type": "String" + }, + { + "name": "calledElementVersionTag", + "isAttr": true, + "type": "String" + }, + { + "name": "calledElementTenantId", + "isAttr": true, + "type": "String" + }, + { + "name": "caseRef", + "isAttr": true, + "type": "String" + }, + { + "name": "caseBinding", + "isAttr": true, + "type": "String", + "default": "latest" + }, + { + "name": "caseVersion", + "isAttr": true, + "type": "String" + }, + { + "name": "caseTenantId", + "isAttr": true, + "type": "String" + }, + { + "name": "variableMappingClass", + "isAttr": true, + "type": "String" + }, + { + "name": "variableMappingDelegateExpression", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "ServiceTaskLike", + "extends": [ + "bpmn:ServiceTask", + "bpmn:BusinessRuleTask", + "bpmn:SendTask", + "bpmn:MessageEventDefinition" + ], + "properties": [ + { + "name": "expression", + "isAttr": true, + "type": "String" + }, + { + "name": "class", + "isAttr": true, + "type": "String" + }, + { + "name": "delegateExpression", + "isAttr": true, + "type": "String" + }, + { + "name": "resultVariable", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "DmnCapable", + "extends": [ + "bpmn:BusinessRuleTask" + ], + "properties": [ + { + "name": "decisionRef", + "isAttr": true, + "type": "String" + }, + { + "name": "decisionRefBinding", + "isAttr": true, + "type": "String", + "default": "latest" + }, + { + "name": "decisionRefVersion", + "isAttr": true, + "type": "String" + }, + { + "name": "mapDecisionResult", + "isAttr": true, + "type": "String", + "default": "resultList" + }, + { + "name": "decisionRefTenantId", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "ExternalCapable", + "extends": [ + "camunda:ServiceTaskLike" + ], + "properties": [ + { + "name": "type", + "isAttr": true, + "type": "String" + }, + { + "name": "topic", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "TaskPriorized", + "extends": [ + "bpmn:Process", + "camunda:ExternalCapable" + ], + "properties": [ + { + "name": "taskPriority", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "Properties", + "superClass": [ + "Element" + ], + "meta": { + "allowedIn": [ "*" ] + }, + "properties": [ + { + "name": "values", + "type": "Property", + "isMany": true + } + ] + }, + { + "name": "Property", + "superClass": [ + "Element" + ], + "properties": [ + { + "name": "id", + "type": "String", + "isAttr": true + }, + { + "name": "name", + "type": "String", + "isAttr": true + }, + { + "name": "value", + "type": "String", + "isAttr": true + } + ] + }, + { + "name": "Connector", + "superClass": [ + "Element" + ], + "meta": { + "allowedIn": [ + "camunda:ServiceTaskLike" + ] + }, + "properties": [ + { + "name": "inputOutput", + "type": "InputOutput" + }, + { + "name": "connectorId", + "type": "String" + } + ] + }, + { + "name": "InputOutput", + "superClass": [ + "Element" + ], + "meta": { + "allowedIn": [ + "bpmn:FlowNode", + "camunda:Connector" + ] + }, + "properties": [ + { + "name": "inputOutput", + "type": "InputOutput" + }, + { + "name": "connectorId", + "type": "String" + }, + { + "name": "inputParameters", + "isMany": true, + "type": "InputParameter" + }, + { + "name": "outputParameters", + "isMany": true, + "type": "OutputParameter" + } + ] + }, + { + "name": "InputOutputParameter", + "properties": [ + { + "name": "name", + "isAttr": true, + "type": "String" + }, + { + "name": "value", + "isBody": true, + "type": "String" + }, + { + "name": "definition", + "type": "InputOutputParameterDefinition" + } + ] + }, + { + "name": "InputOutputParameterDefinition", + "isAbstract": true + }, + { + "name": "List", + "superClass": [ "InputOutputParameterDefinition" ], + "properties": [ + { + "name": "items", + "isMany": true, + "type": "InputOutputParameterDefinition" + } + ] + }, + { + "name": "Map", + "superClass": [ "InputOutputParameterDefinition" ], + "properties": [ + { + "name": "entries", + "isMany": true, + "type": "Entry" + } + ] + }, + { + "name": "Entry", + "properties": [ + { + "name": "key", + "isAttr": true, + "type": "String" + }, + { + "name": "value", + "isBody": true, + "type": "String" + }, + { + "name": "definition", + "type": "InputOutputParameterDefinition" + } + ] + }, + { + "name": "Value", + "superClass": [ + "InputOutputParameterDefinition" + ], + "properties": [ + { + "name": "id", + "isAttr": true, + "type": "String" + }, + { + "name": "name", + "isAttr": true, + "type": "String" + }, + { + "name": "value", + "isBody": true, + "type": "String" + } + ] + }, + { + "name": "Script", + "superClass": [ "InputOutputParameterDefinition" ], + "properties": [ + { + "name": "scriptFormat", + "isAttr": true, + "type": "String" + }, + { + "name": "resource", + "isAttr": true, + "type": "String" + }, + { + "name": "value", + "isBody": true, + "type": "String" + } + ] + }, + { + "name": "Field", + "superClass": [ "Element" ], + "meta": { + "allowedIn": [ + "camunda:ServiceTaskLike", + "camunda:ExecutionListener", + "camunda:TaskListener" + ] + }, + "properties": [ + { + "name": "name", + "isAttr": true, + "type": "String" + }, + { + "name": "expression", + "type": "String" + }, + { + "name": "stringValue", + "isAttr": true, + "type": "String" + }, + { + "name": "string", + "type": "String" + } + ] + }, + { + "name": "InputParameter", + "superClass": [ "InputOutputParameter" ] + }, + { + "name": "OutputParameter", + "superClass": [ "InputOutputParameter" ] + }, + { + "name": "Collectable", + "isAbstract": true, + "extends": [ "bpmn:MultiInstanceLoopCharacteristics" ], + "superClass": [ "camunda:AsyncCapable" ], + "properties": [ + { + "name": "collection", + "isAttr": true, + "type": "String" + }, + { + "name": "elementVariable", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "FailedJobRetryTimeCycle", + "superClass": [ "Element" ], + "meta": { + "allowedIn": [ + "camunda:AsyncCapable", + "bpmn:MultiInstanceLoopCharacteristics" + ] + }, + "properties": [ + { + "name": "body", + "isBody": true, + "type": "String" + } + ] + }, + { + "name": "ExecutionListener", + "superClass": [ "Element" ], + "meta": { + "allowedIn": [ + "bpmn:Task", + "bpmn:ServiceTask", + "bpmn:UserTask", + "bpmn:BusinessRuleTask", + "bpmn:ScriptTask", + "bpmn:ReceiveTask", + "bpmn:ManualTask", + "bpmn:ExclusiveGateway", + "bpmn:SequenceFlow", + "bpmn:ParallelGateway", + "bpmn:InclusiveGateway", + "bpmn:EventBasedGateway", + "bpmn:StartEvent", + "bpmn:IntermediateCatchEvent", + "bpmn:IntermediateThrowEvent", + "bpmn:EndEvent", + "bpmn:BoundaryEvent", + "bpmn:CallActivity", + "bpmn:SubProcess", + "bpmn:Process" + ] + }, + "properties": [ + { + "name": "expression", + "isAttr": true, + "type": "String" + }, + { + "name": "class", + "isAttr": true, + "type": "String" + }, + { + "name": "delegateExpression", + "isAttr": true, + "type": "String" + }, + { + "name": "event", + "isAttr": true, + "type": "String" + }, + { + "name": "script", + "type": "Script" + }, + { + "name": "fields", + "type": "Field", + "isMany": true + } + ] + }, + { + "name": "TaskListener", + "superClass": [ "Element" ], + "meta": { + "allowedIn": [ + "bpmn:UserTask" + ] + }, + "properties": [ + { + "name": "expression", + "isAttr": true, + "type": "String" + }, + { + "name": "class", + "isAttr": true, + "type": "String" + }, + { + "name": "delegateExpression", + "isAttr": true, + "type": "String" + }, + { + "name": "event", + "isAttr": true, + "type": "String" + }, + { + "name": "script", + "type": "Script" + }, + { + "name": "fields", + "type": "Field", + "isMany": true + }, + { + "name": "id", + "type": "String", + "isAttr": true + }, + { + "name": "eventDefinitions", + "type": "bpmn:TimerEventDefinition", + "isMany": true + } + ] + }, + { + "name": "FormProperty", + "superClass": [ "Element" ], + "meta": { + "allowedIn": [ + "bpmn:StartEvent", + "bpmn:UserTask" + ] + }, + "properties": [ + { + "name": "id", + "type": "String", + "isAttr": true + }, + { + "name": "name", + "type": "String", + "isAttr": true + }, + { + "name": "type", + "type": "String", + "isAttr": true + }, + { + "name": "required", + "type": "String", + "isAttr": true + }, + { + "name": "readable", + "type": "String", + "isAttr": true + }, + { + "name": "writable", + "type": "String", + "isAttr": true + }, + { + "name": "variable", + "type": "String", + "isAttr": true + }, + { + "name": "expression", + "type": "String", + "isAttr": true + }, + { + "name": "datePattern", + "type": "String", + "isAttr": true + }, + { + "name": "default", + "type": "String", + "isAttr": true + }, + { + "name": "values", + "type": "Value", + "isMany": true + } + ] + }, + { + "name": "FormData", + "superClass": [ "Element" ], + "meta": { + "allowedIn": [ + "bpmn:StartEvent", + "bpmn:UserTask" + ] + }, + "properties": [ + { + "name": "fields", + "type": "FormField", + "isMany": true + }, + { + "name": "businessKey", + "type": "String", + "isAttr": true + } + ] + }, + { + "name": "FormField", + "superClass": [ "Element" ], + "properties": [ + { + "name": "id", + "type": "String", + "isAttr": true + }, + { + "name": "label", + "type": "String", + "isAttr": true + }, + { + "name": "type", + "type": "String", + "isAttr": true + }, + { + "name": "datePattern", + "type": "String", + "isAttr": true + }, + { + "name": "defaultValue", + "type": "String", + "isAttr": true + }, + { + "name": "properties", + "type": "Properties" + }, + { + "name": "validation", + "type": "Validation" + }, + { + "name": "values", + "type": "Value", + "isMany": true + } + ] + }, + { + "name": "Validation", + "superClass": [ "Element" ], + "properties": [ + { + "name": "constraints", + "type": "Constraint", + "isMany": true + } + ] + }, + { + "name": "Constraint", + "superClass": [ "Element" ], + "properties": [ + { + "name": "name", + "type": "String", + "isAttr": true + }, + { + "name": "config", + "type": "String", + "isAttr": true + } + ] + }, + { + "name": "ConditionalEventDefinition", + "isAbstract": true, + "extends": [ + "bpmn:ConditionalEventDefinition" + ], + "properties": [ + { + "name": "variableName", + "isAttr": true, + "type": "String" + }, + { + "name": "variableEvents", + "isAttr": true, + "type": "String" + } + ] + } + ], + "emumerations": [ ] +} diff --git a/ruoyi-ui/apps/web-antd/src/package/designer/plugins/descriptor/flowableDescriptor.json b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/descriptor/flowableDescriptor.json new file mode 100644 index 0000000..7b66155 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/descriptor/flowableDescriptor.json @@ -0,0 +1,1230 @@ +{ + "name": "Flowable", + "uri": "http://flowable.org/bpmn", + "prefix": "flowable", + "xml": { + "tagAlias": "lowerCase" + }, + "associations": [], + "types": [ + { + "name": "InOutBinding", + "superClass": ["Element"], + "isAbstract": true, + "properties": [ + { + "name": "source", + "isAttr": true, + "type": "String" + }, + { + "name": "sourceExpression", + "isAttr": true, + "type": "String" + }, + { + "name": "target", + "isAttr": true, + "type": "String" + }, + { + "name": "businessKey", + "isAttr": true, + "type": "String" + }, + { + "name": "local", + "isAttr": true, + "type": "Boolean", + "default": false + }, + { + "name": "variables", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "In", + "superClass": ["InOutBinding"], + "meta": { + "allowedIn": ["bpmn:CallActivity"] + } + }, + { + "name": "Out", + "superClass": ["InOutBinding"], + "meta": { + "allowedIn": ["bpmn:CallActivity"] + } + }, + { + "name": "AsyncCapable", + "isAbstract": true, + "extends": ["bpmn:Activity", "bpmn:Gateway", "bpmn:Event"], + "properties": [ + { + "name": "async", + "isAttr": true, + "type": "Boolean", + "default": false + }, + { + "name": "asyncBefore", + "isAttr": true, + "type": "Boolean", + "default": false + }, + { + "name": "asyncAfter", + "isAttr": true, + "type": "Boolean", + "default": false + }, + { + "name": "exclusive", + "isAttr": true, + "type": "Boolean", + "default": true + } + ] + }, + { + "name": "JobPriorized", + "isAbstract": true, + "extends": ["bpmn:Process", "flowable:AsyncCapable"], + "properties": [ + { + "name": "jobPriority", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "SignalEventDefinition", + "isAbstract": true, + "extends": ["bpmn:SignalEventDefinition"], + "properties": [ + { + "name": "async", + "isAttr": true, + "type": "Boolean", + "default": false + } + ] + }, + { + "name": "ErrorEventDefinition", + "isAbstract": true, + "extends": ["bpmn:ErrorEventDefinition"], + "properties": [ + { + "name": "errorCodeVariable", + "isAttr": true, + "type": "String" + }, + { + "name": "errorMessageVariable", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "Error", + "isAbstract": true, + "extends": ["bpmn:Error"], + "properties": [ + { + "name": "flowable:errorMessage", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "PotentialStarter", + "superClass": ["Element"], + "properties": [ + { + "name": "resourceAssignmentExpression", + "type": "bpmn:ResourceAssignmentExpression" + } + ] + }, + { + "name": "FormSupported", + "isAbstract": true, + "extends": ["bpmn:StartEvent", "bpmn:UserTask"], + "properties": [ + { + "name": "formHandlerClass", + "isAttr": true, + "type": "String" + }, + { + "name": "formKey", + "isAttr": true, + "type": "String" + }, + { + "name": "formType", + "isAttr": true, + "type": "String" + }, + { + "name": "formReadOnly", + "isAttr": true, + "type": "Boolean", + "default": false + }, + { + "name": "formInit", + "isAttr": true, + "type": "Boolean", + "default": true + } + ] + }, + { + "name": "TemplateSupported", + "isAbstract": true, + "extends": ["bpmn:Process", "bpmn:FlowElement"], + "properties": [ + { + "name": "modelerTemplate", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "Initiator", + "isAbstract": true, + "extends": ["bpmn:StartEvent"], + "properties": [ + { + "name": "initiator", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "ScriptTask", + "isAbstract": true, + "extends": ["bpmn:ScriptTask"], + "properties": [ + { + "name": "resultVariable", + "isAttr": true, + "type": "String" + }, + { + "name": "resource", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "Process", + "isAbstract": true, + "extends": ["bpmn:Process"], + "properties": [ + { + "name": "candidateStarterGroups", + "isAttr": true, + "type": "String" + }, + { + "name": "candidateStarterUsers", + "isAttr": true, + "type": "String" + }, + { + "name": "versionTag", + "isAttr": true, + "type": "String" + }, + { + "name": "historyTimeToLive", + "isAttr": true, + "type": "String" + }, + { + "name": "isStartableInTasklist", + "isAttr": true, + "type": "Boolean", + "default": true + } + ] + }, + { + "name": "EscalationEventDefinition", + "isAbstract": true, + "extends": ["bpmn:EscalationEventDefinition"], + "properties": [ + { + "name": "escalationCodeVariable", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "FormalExpression", + "isAbstract": true, + "extends": ["bpmn:FormalExpression"], + "properties": [ + { + "name": "resource", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "Assignable", + "extends": ["bpmn:UserTask"], + "properties": [ + { + "name": "assignee", + "isAttr": true, + "type": "String" + }, + { + "name": "candidateUsers", + "isAttr": true, + "type": "String" + }, + { + "name": "candidateGroups", + "isAttr": true, + "type": "String" + }, + { + "name": "dueDate", + "isAttr": true, + "type": "String" + }, + { + "name": "followUpDate", + "isAttr": true, + "type": "String" + }, + { + "name": "priority", + "isAttr": true, + "type": "String" + }, + { + "name": "dataType", + "isAttr": true, + "type": "String" + }, + { + "name": "text", + "isAttr": true, + "type": "String" + }, + { + "name": "deptId", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "Assignee", + "supperClass": "Element", + "meta": { + "allowedIn": ["*"] + }, + "properties": [ + { + "name": "label", + "type": "String", + "isAttr": true + }, + { + "name": "viewId", + "type": "Number", + "isAttr": true + } + ] + }, + { + "name": "CallActivity", + "extends": ["bpmn:CallActivity"], + "properties": [ + { + "name": "calledElementBinding", + "isAttr": true, + "type": "String", + "default": "latest" + }, + { + "name": "calledElementVersion", + "isAttr": true, + "type": "String" + }, + { + "name": "calledElementVersionTag", + "isAttr": true, + "type": "String" + }, + { + "name": "calledElementTenantId", + "isAttr": true, + "type": "String" + }, + { + "name": "caseRef", + "isAttr": true, + "type": "String" + }, + { + "name": "caseBinding", + "isAttr": true, + "type": "String", + "default": "latest" + }, + { + "name": "caseVersion", + "isAttr": true, + "type": "String" + }, + { + "name": "caseTenantId", + "isAttr": true, + "type": "String" + }, + { + "name": "variableMappingClass", + "isAttr": true, + "type": "String" + }, + { + "name": "variableMappingDelegateExpression", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "ServiceTaskLike", + "extends": [ + "bpmn:ServiceTask", + "bpmn:BusinessRuleTask", + "bpmn:SendTask", + "bpmn:MessageEventDefinition" + ], + "properties": [ + { + "name": "expression", + "isAttr": true, + "type": "String" + }, + { + "name": "class", + "isAttr": true, + "type": "String" + }, + { + "name": "delegateExpression", + "isAttr": true, + "type": "String" + }, + { + "name": "resultVariable", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "DmnCapable", + "extends": ["bpmn:BusinessRuleTask"], + "properties": [ + { + "name": "decisionRef", + "isAttr": true, + "type": "String" + }, + { + "name": "decisionRefBinding", + "isAttr": true, + "type": "String", + "default": "latest" + }, + { + "name": "decisionRefVersion", + "isAttr": true, + "type": "String" + }, + { + "name": "mapDecisionResult", + "isAttr": true, + "type": "String", + "default": "resultList" + }, + { + "name": "decisionRefTenantId", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "ExternalCapable", + "extends": ["flowable:ServiceTaskLike"], + "properties": [ + { + "name": "type", + "isAttr": true, + "type": "String" + }, + { + "name": "topic", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "TaskPriorized", + "extends": ["bpmn:Process", "flowable:ExternalCapable"], + "properties": [ + { + "name": "taskPriority", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "Properties", + "superClass": ["Element"], + "meta": { + "allowedIn": ["*"] + }, + "properties": [ + { + "name": "values", + "type": "Property", + "isMany": true + } + ] + }, + { + "name": "Property", + "superClass": ["Element"], + "properties": [ + { + "name": "id", + "type": "String", + "isAttr": true + }, + { + "name": "name", + "type": "String", + "isAttr": true + }, + { + "name": "value", + "type": "String", + "isAttr": true + } + ] + }, + { + "name": "Button", + "superClass": ["Element"], + "meta": { + "allowedIn": ["bpmn:UserTask"] + }, + "properties": [ + { + "name": "id", + "type": "String", + "isAttr": true + }, + { + "name": "name", + "type": "String", + "isAttr": true + }, + { + "name": "code", + "type": "String", + "isAttr": true + }, + { + "name": "isHide", + "type": "String", + "isAttr": true + }, + { + "name": "next", + "type": "String", + "isAttr": true + }, + { + "name": "sort", + "type": "Integer", + "isAttr": true + } + ] + }, + { + "name": "Assignee", + "superClass": ["Element"], + "meta": { + "allowedIn": ["bpmn:UserTask"] + }, + "properties": [ + { + "name": "id", + "type": "String", + "isAttr": true + }, + { + "name": "type", + "type": "String", + "isAttr": true + }, + { + "name": "value", + "type": "String", + "isAttr": true + }, + { + "name": "condition", + "type": "String", + "isAttr": true + }, + { + "name": "operationType", + "type": "String", + "isAttr": true + }, + { + "name": "sort", + "type": "Integer", + "isAttr": true + } + ] + }, + { + "name": "Connector", + "superClass": ["Element"], + "meta": { + "allowedIn": ["flowable:ServiceTaskLike"] + }, + "properties": [ + { + "name": "inputOutput", + "type": "InputOutput" + }, + { + "name": "connectorId", + "type": "String" + } + ] + }, + { + "name": "InputOutput", + "superClass": ["Element"], + "meta": { + "allowedIn": ["bpmn:FlowNode", "flowable:Connector"] + }, + "properties": [ + { + "name": "inputOutput", + "type": "InputOutput" + }, + { + "name": "connectorId", + "type": "String" + }, + { + "name": "inputParameters", + "isMany": true, + "type": "InputParameter" + }, + { + "name": "outputParameters", + "isMany": true, + "type": "OutputParameter" + } + ] + }, + { + "name": "InputOutputParameter", + "properties": [ + { + "name": "name", + "isAttr": true, + "type": "String" + }, + { + "name": "value", + "isBody": true, + "type": "String" + }, + { + "name": "definition", + "type": "InputOutputParameterDefinition" + } + ] + }, + { + "name": "InputOutputParameterDefinition", + "isAbstract": true + }, + { + "name": "List", + "superClass": ["InputOutputParameterDefinition"], + "properties": [ + { + "name": "items", + "isMany": true, + "type": "InputOutputParameterDefinition" + } + ] + }, + { + "name": "Map", + "superClass": ["InputOutputParameterDefinition"], + "properties": [ + { + "name": "entries", + "isMany": true, + "type": "Entry" + } + ] + }, + { + "name": "Entry", + "properties": [ + { + "name": "key", + "isAttr": true, + "type": "String" + }, + { + "name": "value", + "isBody": true, + "type": "String" + }, + { + "name": "definition", + "type": "InputOutputParameterDefinition" + } + ] + }, + { + "name": "Value", + "superClass": ["InputOutputParameterDefinition"], + "properties": [ + { + "name": "id", + "isAttr": true, + "type": "String" + }, + { + "name": "name", + "isAttr": true, + "type": "String" + }, + { + "name": "value", + "isBody": true, + "type": "String" + } + ] + }, + { + "name": "Script", + "superClass": ["InputOutputParameterDefinition"], + "properties": [ + { + "name": "scriptFormat", + "isAttr": true, + "type": "String" + }, + { + "name": "resource", + "isAttr": true, + "type": "String" + }, + { + "name": "value", + "isBody": true, + "type": "String" + } + ] + }, + { + "name": "Field", + "superClass": ["Element"], + "meta": { + "allowedIn": [ + "flowable:ServiceTaskLike", + "flowable:ExecutionListener", + "flowable:TaskListener", + "bpmn:ServiceTask" + ] + }, + "properties": [ + { + "name": "name", + "isAttr": true, + "type": "String" + }, + { + "name": "expression", + "type": "String" + }, + { + "name": "stringValue", + "isAttr": true, + "type": "String" + }, + { + "name": "string", + "type": "String" + }, + { + "name": "htmlVar", + "type": "Expression" + } + ] + }, + { + "name": "ChildField", + "superClass": ["Element"], + "properties": [ + { + "name": "id", + "type": "String", + "isAttr": true + }, + { + "name": "name", + "type": "String", + "isAttr": true + }, + { + "name": "type", + "type": "String", + "isAttr": true + }, + { + "name": "required", + "type": "String", + "isAttr": true + }, + { + "name": "readable", + "type": "String", + "isAttr": true + }, + { + "name": "writable", + "type": "String", + "isAttr": true + }, + { + "name": "variable", + "type": "String", + "isAttr": true + }, + { + "name": "expression", + "type": "String", + "isAttr": true + }, + { + "name": "datePattern", + "type": "String", + "isAttr": true + }, + { + "name": "default", + "type": "String", + "isAttr": true + }, + { + "name": "values", + "type": "Value", + "isMany": true + } + ] + }, + { + "name": "InputParameter", + "superClass": ["InputOutputParameter"] + }, + { + "name": "OutputParameter", + "superClass": ["InputOutputParameter"] + }, + { + "name": "Collectable", + "isAbstract": true, + "extends": ["bpmn:MultiInstanceLoopCharacteristics"], + "superClass": ["flowable:AsyncCapable"], + "properties": [ + { + "name": "collection", + "isAttr": true, + "type": "String" + }, + { + "name": "elementVariable", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "FailedJobRetryTimeCycle", + "superClass": ["Element"], + "meta": { + "allowedIn": [ + "flowable:AsyncCapable", + "bpmn:MultiInstanceLoopCharacteristics" + ] + }, + "properties": [ + { + "name": "body", + "isBody": true, + "type": "String" + } + ] + }, + { + "name": "ExecutionListener", + "superClass": ["Element"], + "meta": { + "allowedIn": [ + "bpmn:Task", + "bpmn:ServiceTask", + "bpmn:UserTask", + "bpmn:BusinessRuleTask", + "bpmn:ScriptTask", + "bpmn:ReceiveTask", + "bpmn:ManualTask", + "bpmn:ExclusiveGateway", + "bpmn:SequenceFlow", + "bpmn:ParallelGateway", + "bpmn:InclusiveGateway", + "bpmn:EventBasedGateway", + "bpmn:StartEvent", + "bpmn:IntermediateCatchEvent", + "bpmn:IntermediateThrowEvent", + "bpmn:EndEvent", + "bpmn:BoundaryEvent", + "bpmn:CallActivity", + "bpmn:SubProcess", + "bpmn:Process" + ] + }, + "properties": [ + { + "name": "expression", + "isAttr": true, + "type": "String" + }, + { + "name": "class", + "isAttr": true, + "type": "String" + }, + { + "name": "delegateExpression", + "isAttr": true, + "type": "String" + }, + { + "name": "event", + "isAttr": true, + "type": "String" + }, + { + "name": "script", + "type": "Script" + }, + { + "name": "fields", + "type": "Field", + "isMany": true + } + ] + }, + { + "name": "TaskListener", + "superClass": ["Element"], + "meta": { + "allowedIn": ["bpmn:UserTask"] + }, + "properties": [ + { + "name": "expression", + "isAttr": true, + "type": "String" + }, + { + "name": "class", + "isAttr": true, + "type": "String" + }, + { + "name": "delegateExpression", + "isAttr": true, + "type": "String" + }, + { + "name": "event", + "isAttr": true, + "type": "String" + }, + { + "name": "script", + "type": "Script" + }, + { + "name": "fields", + "type": "Field", + "isMany": true + } + ] + }, + { + "name": "FormProperty", + "superClass": ["Element"], + "meta": { + "allowedIn": ["bpmn:StartEvent", "bpmn:UserTask"] + }, + "properties": [ + { + "name": "id", + "type": "String", + "isAttr": true + }, + { + "name": "name", + "type": "String", + "isAttr": true + }, + { + "name": "type", + "type": "String", + "isAttr": true + }, + { + "name": "required", + "type": "String", + "isAttr": true + }, + { + "name": "readable", + "type": "String", + "isAttr": true + }, + { + "name": "writable", + "type": "String", + "isAttr": true + }, + { + "name": "variable", + "type": "String", + "isAttr": true + }, + { + "name": "expression", + "type": "String", + "isAttr": true + }, + { + "name": "datePattern", + "type": "String", + "isAttr": true + }, + { + "name": "default", + "type": "String", + "isAttr": true + }, + { + "name": "values", + "type": "Value", + "isMany": true + }, + { + "name": "children", + "type": "ChildField", + "isMany": true + }, + { + "name": "extensionElements", + "type": "bpmn:ExtensionElements", + "isMany": true + } + ] + }, + { + "name": "FormData", + "superClass": ["Element"], + "meta": { + "allowedIn": ["bpmn:StartEvent", "bpmn:UserTask"] + }, + "properties": [ + { + "name": "fields", + "type": "FormField", + "isMany": true + }, + { + "name": "businessKey", + "type": "String", + "isAttr": true + } + ] + }, + { + "name": "FormField", + "superClass": ["Element"], + "properties": [ + { + "name": "id", + "type": "String", + "isAttr": true + }, + { + "name": "label", + "type": "String", + "isAttr": true + }, + { + "name": "type", + "type": "String", + "isAttr": true + }, + { + "name": "datePattern", + "type": "String", + "isAttr": true + }, + { + "name": "defaultValue", + "type": "String", + "isAttr": true + }, + { + "name": "properties", + "type": "Properties" + }, + { + "name": "validation", + "type": "Validation" + }, + { + "name": "values", + "type": "Value", + "isMany": true + } + ] + }, + { + "name": "Validation", + "superClass": ["Element"], + "properties": [ + { + "name": "constraints", + "type": "Constraint", + "isMany": true + } + ] + }, + { + "name": "Constraint", + "superClass": ["Element"], + "properties": [ + { + "name": "name", + "type": "String", + "isAttr": true + }, + { + "name": "config", + "type": "String", + "isAttr": true + } + ] + }, + { + "name": "ConditionalEventDefinition", + "isAbstract": true, + "extends": ["bpmn:ConditionalEventDefinition"], + "properties": [ + { + "name": "variableName", + "isAttr": true, + "type": "String" + }, + { + "name": "variableEvent", + "isAttr": true, + "type": "String" + } + ] + }, + { + "name": "Condition", + "superClass": ["Element"], + "meta": { + "allowedIn": ["bpmn:SequenceFlow"] + }, + "properties": [ + { + "name": "id", + "type": "String", + "isAttr": true + }, + { + "name": "field", + "type": "String", + "isAttr": true + }, + { + "name": "compare", + "type": "String", + "isAttr": true + }, + { + "name": "value", + "type": "String", + "isAttr": true + }, + { + "name": "logic", + "type": "String", + "isAttr": true + }, + { + "name": "sort", + "type": "Integer", + "isAttr": true + } + ] + } + ], + "emumerations": [] +} diff --git a/ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/activiti/activitiExtension.js b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/activiti/activitiExtension.js new file mode 100644 index 0000000..cfa5589 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/activiti/activitiExtension.js @@ -0,0 +1,73 @@ +'use strict'; + +import { some } from '#/utils/min-dash.js'; + +var ALLOWED_TYPES = { + FailedJobRetryTimeCycle: ['bpmn:StartEvent', 'bpmn:BoundaryEvent', 'bpmn:IntermediateCatchEvent', 'bpmn:Activity'], + Connector: ['bpmn:EndEvent', 'bpmn:IntermediateThrowEvent'], + Field: ['bpmn:EndEvent', 'bpmn:IntermediateThrowEvent'] +}; + +function is(element, type) { + return element && typeof element.$instanceOf === 'function' && element.$instanceOf(type); +} + +function exists(element) { + return element && element.length; +} + +function includesType(collection, type) { + return ( + exists(collection) && + some(collection, function(element) { + return is(element, type); + }) + ); +} + +function anyType(element, types) { + return some(types, function(type) { + return is(element, type); + }); +} + +function isAllowed(propName, propDescriptor, newElement) { + var name = propDescriptor.name; + var types = ALLOWED_TYPES[name.replace(/activiti:/, '')]; + + return name === propName && anyType(newElement, types); +} + +export default function ActivitiModdleExtension(eventBus) { + eventBus.on( + 'property.clone', + function(context) { + var newElement = context.newElement; + var propDescriptor = context.propertyDescriptor; + + this.canCloneProperty(newElement, propDescriptor); + }, + this + ); +} + +ActivitiModdleExtension.$inject = ['eventBus']; + +ActivitiModdleExtension.prototype.canCloneProperty = function(newElement, propDescriptor) { + if (isAllowed('activiti:FailedJobRetryTimeCycle', propDescriptor, newElement)) { + return ( + includesType(newElement.eventDefinitions, 'bpmn:TimerEventDefinition') || + includesType(newElement.eventDefinitions, 'bpmn:SignalEventDefinition') || + is(newElement.loopCharacteristics, 'bpmn:MultiInstanceLoopCharacteristics') + ); + } + + if (isAllowed('activiti:Connector', propDescriptor, newElement)) { + return includesType(newElement.eventDefinitions, 'bpmn:MessageEventDefinition'); + } + + if (isAllowed('activiti:Field', propDescriptor, newElement)) { + return includesType(newElement.eventDefinitions, 'bpmn:MessageEventDefinition'); + } +}; + diff --git a/ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/activiti/index.js b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/activiti/index.js new file mode 100644 index 0000000..9132e5a --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/activiti/index.js @@ -0,0 +1,10 @@ +/* + * @author igdianov + * address https://github.com/igdianov/activiti-bpmn-moddle + * */ + +import ActivitiModdleExtension from './activitiExtension.js' +export default { + __init__: ['ActivitiModdleExtension'], + ActivitiModdleExtension: ['type', ActivitiModdleExtension] +}; diff --git a/ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/camunda/extension.js b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/camunda/extension.js new file mode 100644 index 0000000..307766e --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/camunda/extension.js @@ -0,0 +1,144 @@ +'use strict'; + +import { some, isObject, isFunction } from '#/utils/min-dash.js'; + +var WILDCARD = '*'; + +export default function CamundaModdleExtension(eventBus) { + var self = this; + + eventBus.on('moddleCopy.canCopyProperty', function(context) { + var property = context.property; + var parent = context.parent; + + return self.canCopyProperty(property, parent); + }); +} + +CamundaModdleExtension.$inject = ['eventBus']; + +/** + * Check wether to disallow copying property. + */ +CamundaModdleExtension.prototype.canCopyProperty = function(property, parent) { + // (1) check wether property is allowed in parent + if (isObject(property) && !isAllowedInParent(property, parent)) { + return false; + } + + // (2) check more complex scenarios + + if (is(property, 'camunda:InputOutput') && !this.canHostInputOutput(parent)) { + return false; + } + + if (isAny(property, ['camunda:Connector', 'camunda:Field']) && !this.canHostConnector(parent)) { + return false; + } + + if (is(property, 'camunda:In') && !this.canHostIn(parent)) { + return false; + } +}; + +CamundaModdleExtension.prototype.canHostInputOutput = function(parent) { + // allowed in camunda:Connector + var connector = getParent(parent, 'camunda:Connector'); + + if (connector) { + return true; + } + + // special rules inside bpmn:FlowNode + var flowNode = getParent(parent, 'bpmn:FlowNode'); + + if (!flowNode) { + return false; + } + + if (isAny(flowNode, ['bpmn:StartEvent', 'bpmn:Gateway', 'bpmn:BoundaryEvent'])) { + return false; + } + + if (is(flowNode, 'bpmn:SubProcess') && flowNode.get('triggeredByEvent')) { + return false; + } + + return true; +}; + +CamundaModdleExtension.prototype.canHostConnector = function(parent) { + var serviceTaskLike = getParent(parent, 'camunda:ServiceTaskLike'); + + if (is(serviceTaskLike, 'bpmn:MessageEventDefinition')) { + // only allow on throw and end events + return getParent(parent, 'bpmn:IntermediateThrowEvent') || getParent(parent, 'bpmn:EndEvent'); + } + + return true; +}; + +CamundaModdleExtension.prototype.canHostIn = function(parent) { + var callActivity = getParent(parent, 'bpmn:CallActivity'); + + if (callActivity) { + return true; + } + + var signalEventDefinition = getParent(parent, 'bpmn:SignalEventDefinition'); + + if (signalEventDefinition) { + // only allow on throw and end events + return getParent(parent, 'bpmn:IntermediateThrowEvent') || getParent(parent, 'bpmn:EndEvent'); + } + + return true; +}; + +// helpers ////////// + +function is(element, type) { + return element && isFunction(element.$instanceOf) && element.$instanceOf(type); +} + +function isAny(element, types) { + return some(types, function(t) { + return is(element, t); + }); +} + +function getParent(element, type) { + if (!type) { + return element.$parent; + } + + if (is(element, type)) { + return element; + } + + if (!element.$parent) { + return; + } + + return getParent(element.$parent, type); +} + +function isAllowedInParent(property, parent) { + // (1) find property descriptor + var descriptor = property.$type && property.$model.getTypeDescriptor(property.$type); + + var allowedIn = descriptor && descriptor.meta && descriptor.meta.allowedIn; + + if (!allowedIn || isWildcard(allowedIn)) { + return true; + } + + // (2) check wether property has parent of allowed type + return some(allowedIn, function(type) { + return getParent(parent, type); + }); +} + +function isWildcard(allowedIn) { + return allowedIn.indexOf(WILDCARD) !== -1; +} diff --git a/ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/camunda/index.js b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/camunda/index.js new file mode 100644 index 0000000..007c1f4 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/camunda/index.js @@ -0,0 +1,7 @@ +'use strict'; + +import CamundaModdleExtension from './extension.js'; +export default { + __init__: ['CamundaModdleExtension'], + CamundaModdleExtension: ['type', CamundaModdleExtension] +}; diff --git a/ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/flowable/flowableExtension.js b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/flowable/flowableExtension.js new file mode 100644 index 0000000..d214d9a --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/flowable/flowableExtension.js @@ -0,0 +1,72 @@ +'use strict'; + +import { some } from '#/utils/min-dash.js'; + +var ALLOWED_TYPES = { + FailedJobRetryTimeCycle: ['bpmn:StartEvent', 'bpmn:BoundaryEvent', 'bpmn:IntermediateCatchEvent', 'bpmn:Activity'], + Connector: ['bpmn:EndEvent', 'bpmn:IntermediateThrowEvent'], + Field: ['bpmn:EndEvent', 'bpmn:IntermediateThrowEvent'] +}; + +function is(element, type) { + return element && typeof element.$instanceOf === 'function' && element.$instanceOf(type); +} + +function exists(element) { + return element && element.length; +} + +function includesType(collection, type) { + return ( + exists(collection) && + some(collection, function(element) { + return is(element, type); + }) + ); +} + +function anyType(element, types) { + return some(types, function(type) { + return is(element, type); + }); +} + +function isAllowed(propName, propDescriptor, newElement) { + var name = propDescriptor.name; + var types = ALLOWED_TYPES[name.replace(/flowable:/, '')]; + + return name === propName && anyType(newElement, types); +} + +export default function FlowableModdleExtension(eventBus) { + eventBus.on( + 'property.clone', + function(context) { + var newElement = context.newElement; + var propDescriptor = context.propertyDescriptor; + + this.canCloneProperty(newElement, propDescriptor); + }, + this + ); +} + +FlowableModdleExtension.$inject = ['eventBus']; + +FlowableModdleExtension.prototype.canCloneProperty = function(newElement, propDescriptor) { + if (isAllowed('flowable:FailedJobRetryTimeCycle', propDescriptor, newElement)) { + return ( + includesType(newElement.eventDefinitions, 'bpmn:TimerEventDefinition') || + includesType(newElement.eventDefinitions, 'bpmn:SignalEventDefinition') || + is(newElement.loopCharacteristics, 'bpmn:MultiInstanceLoopCharacteristics') + ); + } + + if (isAllowed('flowable:Connector', propDescriptor, newElement)) { + return includesType(newElement.eventDefinitions, 'bpmn:MessageEventDefinition'); + } + + if (isAllowed('flowable:Field', propDescriptor, newElement)) { + return includesType(newElement.eventDefinitions, 'bpmn:MessageEventDefinition'); + } +}; diff --git a/ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/flowable/index.js b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/flowable/index.js new file mode 100644 index 0000000..c0c3968 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/extension-moddle/flowable/index.js @@ -0,0 +1,10 @@ +/* + * @author igdianov + * address https://github.com/igdianov/activiti-bpmn-moddle + * */ + +import FlowableModdleExtension from './flowableExtension.js' +export default { + __init__: ['FlowableModdleExtension'], + FlowableModdleExtension: ['type', FlowableModdleExtension] +}; diff --git a/ruoyi-ui/apps/web-antd/src/package/designer/plugins/palette/CustomPalette.js b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/palette/CustomPalette.js new file mode 100644 index 0000000..6617bb5 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/palette/CustomPalette.js @@ -0,0 +1,156 @@ +import PaletteProvider from 'bpmn-js/lib/features/palette/PaletteProvider'; +import { assign } from '#/utils/min-dash.js'; + +export default function CustomPalette(palette, create, elementFactory, spaceTool, lassoTool, handTool, globalConnect, translate) { + PaletteProvider.call(this, palette, create, elementFactory, spaceTool, lassoTool, handTool, globalConnect, translate, 2000); +} + +const F = function() {}; // 鏍稿績锛屽埄鐢ㄧ┖瀵硅薄浣滀负涓粙锛� +F.prototype = PaletteProvider.prototype; // 鏍稿績锛屽皢鐖剁被鐨勫師鍨嬭祴鍊肩粰绌哄璞锛� + +// 鍒╃敤涓粙鍑芥暟閲嶅啓鍘熷瀷閾炬柟娉� +F.prototype.getPaletteEntries = function() { + var actions = {}; + var create = this._create; + var elementFactory = this._elementFactory; + var spaceTool = this._spaceTool; + var lassoTool = this._lassoTool; + var handTool = this._handTool; + var globalConnect = this._globalConnect; + var translate = this._translate; + + function createAction(type, group, className, title, options) { + function createListener(event) { + var shape = elementFactory.createShape(assign({ type: type }, options)); + + if (options) { + shape.businessObject.di.isExpanded = options.isExpanded; + } + + create.start(event, shape); + } + + var shortType = type.replace(/^bpmn:/, ''); + + return { + group: group, + className: className, + title: title || translate('Create {type}', { type: shortType }), + action: { + dragstart: createListener, + click: createListener + } + }; + } + + function createSubprocess(event) { + var subProcess = elementFactory.createShape({ + type: 'bpmn:SubProcess', + x: 0, + y: 0, + isExpanded: true + }); + + var startEvent = elementFactory.createShape({ + type: 'bpmn:StartEvent', + x: 40, + y: 82, + parent: subProcess + }); + + create.start(event, [subProcess, startEvent], { + hints: { + autoSelect: [startEvent] + } + }); + } + + function createParticipant(event) { + create.start(event, elementFactory.createParticipantShape()); + } + + assign(actions, { + 'hand-tool': { + group: 'tools', + className: 'bpmn-icon-hand-tool', + title: translate('Activate the hand tool'), + action: { + click: function(event) { + handTool.activateHand(event); + } + } + }, + 'lasso-tool': { + group: 'tools', + className: 'bpmn-icon-lasso-tool', + title: translate('Activate the lasso tool'), + action: { + click: function(event) { + lassoTool.activateSelection(event); + } + } + }, + 'space-tool': { + group: 'tools', + className: 'bpmn-icon-space-tool', + title: translate('Activate the create/remove space tool'), + action: { + click: function(event) { + spaceTool.activateSelection(event); + } + } + }, + 'global-connect-tool': { + group: 'tools', + className: 'bpmn-icon-connection-multi', + title: translate('Activate the global connect tool'), + action: { + click: function(event) { + globalConnect.toggle(event); + } + } + }, + 'tool-separator': { + group: 'tools', + separator: true + }, + 'create.start-event': createAction('bpmn:StartEvent', 'event', 'bpmn-icon-start-event-none', translate('Create StartEvent')), + 'create.intermediate-event': createAction( + 'bpmn:IntermediateThrowEvent', + 'event', + 'bpmn-icon-intermediate-event-none', + translate('Create Intermediate/Boundary Event') + ), + 'create.end-event': createAction('bpmn:EndEvent', 'event', 'bpmn-icon-end-event-none', translate('Create EndEvent')), + 'create.exclusive-gateway': createAction('bpmn:ExclusiveGateway', 'gateway', 'bpmn-icon-gateway-none', translate('Create Gateway')), + 'create.user-task': createAction('bpmn:UserTask', 'activity', 'bpmn-icon-user-task', translate('Create User Task')), + 'create.data-object': createAction('bpmn:DataObjectReference', 'data-object', 'bpmn-icon-data-object', translate('Create DataObjectReference')), + 'create.data-store': createAction('bpmn:DataStoreReference', 'data-store', 'bpmn-icon-data-store', translate('Create DataStoreReference')), + 'create.subprocess-expanded': { + group: 'activity', + className: 'bpmn-icon-subprocess-expanded', + title: translate('Create expanded SubProcess'), + action: { + dragstart: createSubprocess, + click: createSubprocess + } + }, + 'create.participant-expanded': { + group: 'collaboration', + className: 'bpmn-icon-participant', + title: translate('Create Pool/Participant'), + action: { + dragstart: createParticipant, + click: createParticipant + } + }, + 'create.group': createAction('bpmn:Group', 'artifact', 'bpmn-icon-group', translate('Create Group')) + }); + + return actions; +}; + +CustomPalette.$inject = ['palette', 'create', 'elementFactory', 'spaceTool', 'lassoTool', 'handTool', 'globalConnect', 'translate']; + +CustomPalette.prototype = new F(); // 鏍稿績锛屽皢 F鐨勫疄渚嬭祴鍊肩粰瀛愮被锛� +CustomPalette.prototype.constructor = CustomPalette; // 淇瀛愮被CustomPalette鐨勬瀯閫犲櫒鎸囧悜锛岄槻姝㈠師鍨嬮摼鐨勬贩涔憋紱 diff --git a/ruoyi-ui/apps/web-antd/src/package/designer/plugins/palette/index.js b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/palette/index.js new file mode 100644 index 0000000..8d5f03f --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/palette/index.js @@ -0,0 +1,6 @@ +import CustomPalette from './CustomPalette'; + +export default { + __init__: ['customPalette'], + customPalette: ['type', CustomPalette] +}; diff --git a/ruoyi-ui/apps/web-antd/src/package/designer/plugins/palette/paletteProvider.js b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/palette/paletteProvider.js new file mode 100644 index 0000000..3ac193a --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/palette/paletteProvider.js @@ -0,0 +1,160 @@ +import { assign } from '#/utils/min-dash.js'; + +/** + * A palette provider for BPMN 2.0 elements. + */ +export default function PaletteProvider(palette, create, elementFactory, spaceTool, lassoTool, handTool, globalConnect, translate) { + this._palette = palette; + this._create = create; + this._elementFactory = elementFactory; + this._spaceTool = spaceTool; + this._lassoTool = lassoTool; + this._handTool = handTool; + this._globalConnect = globalConnect; + this._translate = translate; + + palette.registerProvider(this); +} + +PaletteProvider.$inject = ['palette', 'create', 'elementFactory', 'spaceTool', 'lassoTool', 'handTool', 'globalConnect', 'translate']; + +PaletteProvider.prototype.getPaletteEntries = function() { + var actions = {}; + var create = this._create; + var elementFactory = this._elementFactory; + var spaceTool = this._spaceTool; + var lassoTool = this._lassoTool; + var handTool = this._handTool; + var globalConnect = this._globalConnect; + var translate = this._translate; + + function createAction(type, group, className, title, options) { + function createListener(event) { + var shape = elementFactory.createShape(assign({ type: type }, options)); + + if (options) { + shape.businessObject.di.isExpanded = options.isExpanded; + } + + create.start(event, shape); + } + + var shortType = type.replace(/^bpmn:/, ''); + + return { + group: group, + className: className, + title: title || translate('Create {type}', { type: shortType }), + action: { + dragstart: createListener, + click: createListener + } + }; + } + + function createSubprocess(event) { + var subProcess = elementFactory.createShape({ + type: 'bpmn:SubProcess', + x: 0, + y: 0, + isExpanded: true + }); + + var startEvent = elementFactory.createShape({ + type: 'bpmn:StartEvent', + x: 40, + y: 82, + parent: subProcess + }); + + create.start(event, [subProcess, startEvent], { + hints: { + autoSelect: [startEvent] + } + }); + } + + function createParticipant(event) { + create.start(event, elementFactory.createParticipantShape()); + } + + assign(actions, { + 'hand-tool': { + group: 'tools', + className: 'bpmn-icon-hand-tool', + title: translate('Activate the hand tool'), + action: { + click: function(event) { + handTool.activateHand(event); + } + } + }, + 'lasso-tool': { + group: 'tools', + className: 'bpmn-icon-lasso-tool', + title: translate('Activate the lasso tool'), + action: { + click: function(event) { + lassoTool.activateSelection(event); + } + } + }, + 'space-tool': { + group: 'tools', + className: 'bpmn-icon-space-tool', + title: translate('Activate the create/remove space tool'), + action: { + click: function(event) { + spaceTool.activateSelection(event); + } + } + }, + 'global-connect-tool': { + group: 'tools', + className: 'bpmn-icon-connection-multi', + title: translate('Activate the global connect tool'), + action: { + click: function(event) { + globalConnect.toggle(event); + } + } + }, + 'tool-separator': { + group: 'tools', + separator: true + }, + 'create.start-event': createAction('bpmn:StartEvent', 'event', 'bpmn-icon-start-event-none', translate('Create StartEvent')), + 'create.intermediate-event': createAction( + 'bpmn:IntermediateThrowEvent', + 'event', + 'bpmn-icon-intermediate-event-none', + translate('Create Intermediate/Boundary Event') + ), + 'create.end-event': createAction('bpmn:EndEvent', 'event', 'bpmn-icon-end-event-none', translate('Create EndEvent')), + 'create.exclusive-gateway': createAction('bpmn:ExclusiveGateway', 'gateway', 'bpmn-icon-gateway-none', translate('Create Gateway')), + 'create.user-task': createAction('bpmn:UserTask', 'activity', 'bpmn-icon-user-task', translate('Create User Task')), + 'create.data-object': createAction('bpmn:DataObjectReference', 'data-object', 'bpmn-icon-data-object', translate('Create DataObjectReference')), + 'create.data-store': createAction('bpmn:DataStoreReference', 'data-store', 'bpmn-icon-data-store', translate('Create DataStoreReference')), + 'create.subprocess-expanded': { + group: 'activity', + className: 'bpmn-icon-subprocess-expanded', + title: translate('Create expanded SubProcess'), + action: { + dragstart: createSubprocess, + click: createSubprocess + } + }, + 'create.participant-expanded': { + group: 'collaboration', + className: 'bpmn-icon-participant', + title: translate('Create Pool/Participant'), + action: { + dragstart: createParticipant, + click: createParticipant + } + }, + 'create.group': createAction('bpmn:Group', 'artifact', 'bpmn-icon-group', translate('Create Group')) + }); + + return actions; +}; diff --git a/ruoyi-ui/apps/web-antd/src/package/designer/plugins/translate/customTranslate.js b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/translate/customTranslate.js new file mode 100644 index 0000000..12cae81 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/translate/customTranslate.js @@ -0,0 +1,41 @@ +// import translations from "./zh"; +// +// export default function customTranslate(template, replacements) { +// replacements = replacements || {}; +// +// // Translate +// template = translations[template] || template; +// +// // Replace +// return template.replace(/{([^}]+)}/g, function(_, key) { +// let str = replacements[key]; +// if ( +// translations[replacements[key]] !== null && +// translations[replacements[key]] !== "undefined" +// ) { +// // eslint-disable-next-line no-mixed-spaces-and-tabs +// str = translations[replacements[key]]; +// // eslint-disable-next-line no-mixed-spaces-and-tabs +// } +// return str || "{" + key + "}"; +// }); +// } + +export default function customTranslate(translations) { + return function(template, replacements) { + replacements = replacements || {}; + // Translate + template = translations[template] || template; + + // Replace + return template.replace(/{([^}]+)}/g, function(_, key) { + let str = replacements[key]; + if (translations[replacements[key]] !== null && translations[replacements[key]] !== undefined) { + // eslint-disable-next-line no-mixed-spaces-and-tabs + str = translations[replacements[key]]; + // eslint-disable-next-line no-mixed-spaces-and-tabs + } + return str || '{' + key + '}'; + }); + }; +} diff --git a/ruoyi-ui/apps/web-antd/src/package/designer/plugins/translate/zh.js b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/translate/zh.js new file mode 100644 index 0000000..03b20ab --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/designer/plugins/translate/zh.js @@ -0,0 +1,238 @@ +/** + * This is a sample file that should be replaced with the actual translation. + * + * Checkout https://github.com/bpmn-io/bpmn-js-i18n for a list of available + * translations and labels to translate. + */ +export default { + // 娣诲姞閮ㄥ垎 + 'Append EndEvent': '杩藉姞缁撴潫浜嬩欢', + 'Append Gateway': '杩藉姞缃戝叧', + 'Append Task': '杩藉姞浠诲姟', + 'Append Intermediate/Boundary Event': '杩藉姞涓棿鎶涘嚭浜嬩欢/杈圭晫浜嬩欢', + + 'Activate the global connect tool': '婵�娲诲叏灞�杩炴帴宸ュ叿', + 'Append {type}': '娣诲姞 {type}', + 'Add Lane above': '鍦ㄤ笂闈㈡坊鍔犻亾', + 'Divide into two Lanes': '鍒嗗壊鎴愪袱涓亾', + 'Divide into three Lanes': '鍒嗗壊鎴愪笁涓亾', + 'Add Lane below': '鍦ㄤ笅闈㈡坊鍔犻亾', + 'Append compensation activity': '杩藉姞琛ュ伩娲诲姩', + 'Change type': '淇敼绫诲瀷', + 'Connect using Association': '浣跨敤鍏宠仈杩炴帴', + 'Connect using Sequence/MessageFlow or Association': '浣跨敤椤哄簭/娑堟伅娴佹垨鑰呭叧鑱旇繛鎺�', + 'Connect using DataInputAssociation': '浣跨敤鏁版嵁杈撳叆鍏宠仈杩炴帴', + Remove: '绉婚櫎', + 'Activate the hand tool': '婵�娲绘姄鎵嬪伐鍏�', + 'Activate the lasso tool': '婵�娲诲绱㈠伐鍏�', + 'Activate the create/remove space tool': '婵�娲诲垱寤�/鍒犻櫎绌洪棿宸ュ叿', + 'Create expanded SubProcess': '鍒涘缓鎵╁睍瀛愯繃绋�', + 'Create IntermediateThrowEvent/BoundaryEvent': '鍒涘缓涓棿鎶涘嚭浜嬩欢/杈圭晫浜嬩欢', + 'Create Pool/Participant': '鍒涘缓姹�/鍙備笌鑰�', + 'Parallel Multi Instance': '骞惰澶氶噸浜嬩欢', + 'Sequential Multi Instance': '鏃跺簭澶氶噸浜嬩欢', + DataObjectReference: '鏁版嵁瀵硅薄鍙傝��', + DataStoreReference: '鏁版嵁瀛樺偍鍙傝��', + Loop: '寰幆', + 'Ad-hoc': '鍗冲腑', + 'Create {type}': '鍒涘缓 {type}', + Task: '浠诲姟', + 'Send Task': '鍙戦�佷换鍔�', + 'Receive Task': '鎺ユ敹浠诲姟', + 'User Task': '鐢ㄦ埛浠诲姟', + 'Manual Task': '鎵嬪伐浠诲姟', + 'Business Rule Task': '涓氬姟瑙勫垯浠诲姟', + 'Service Task': '鏈嶅姟浠诲姟', + 'Script Task': '鑴氭湰浠诲姟', + 'Call Activity': '璋冪敤娲诲姩', + 'Sub Process (collapsed)': '瀛愭祦绋嬶紙鎶樺彔鐨勶級', + 'Sub Process (expanded)': '瀛愭祦绋嬶紙灞曞紑鐨勶級', + 'Start Event': '寮�濮嬩簨浠�', + StartEvent: '寮�濮嬩簨浠�', + 'Intermediate Throw Event': '涓棿浜嬩欢', + 'End Event': '缁撴潫浜嬩欢', + EndEvent: '缁撴潫浜嬩欢', + 'Create StartEvent': '鍒涘缓寮�濮嬩簨浠�', + 'Create EndEvent': '鍒涘缓缁撴潫浜嬩欢', + 'Create Task': '鍒涘缓浠诲姟', + 'Create User Task': '鍒涘缓鐢ㄦ埛浠诲姟', + 'Create Gateway': '鍒涘缓缃戝叧', + 'Create DataObjectReference': '鍒涘缓鏁版嵁瀵硅薄', + 'Create DataStoreReference': '鍒涘缓鏁版嵁瀛樺偍', + 'Create Group': '鍒涘缓鍒嗙粍', + 'Create Intermediate/Boundary Event': '鍒涘缓涓棿/杈圭晫浜嬩欢', + 'Message Start Event': '娑堟伅寮�濮嬩簨浠�', + 'Timer Start Event': '瀹氭椂寮�濮嬩簨浠�', + 'Conditional Start Event': '鏉′欢寮�濮嬩簨浠�', + 'Signal Start Event': '淇″彿寮�濮嬩簨浠�', + 'Error Start Event': '閿欒寮�濮嬩簨浠�', + 'Escalation Start Event': '鍗囩骇寮�濮嬩簨浠�', + 'Compensation Start Event': '琛ュ伩寮�濮嬩簨浠�', + 'Message Start Event (non-interrupting)': '娑堟伅寮�濮嬩簨浠讹紙闈炰腑鏂級', + 'Timer Start Event (non-interrupting)': '瀹氭椂寮�濮嬩簨浠讹紙闈炰腑鏂級', + 'Conditional Start Event (non-interrupting)': '鏉′欢寮�濮嬩簨浠讹紙闈炰腑鏂級', + 'Signal Start Event (non-interrupting)': '淇″彿寮�濮嬩簨浠讹紙闈炰腑鏂級', + 'Escalation Start Event (non-interrupting)': '鍗囩骇寮�濮嬩簨浠讹紙闈炰腑鏂級', + 'Message Intermediate Catch Event': '娑堟伅涓棿鎹曡幏浜嬩欢', + 'Message Intermediate Throw Event': '娑堟伅涓棿鎶涘嚭浜嬩欢', + 'Timer Intermediate Catch Event': '瀹氭椂涓棿鎹曡幏浜嬩欢', + 'Escalation Intermediate Throw Event': '鍗囩骇涓棿鎶涘嚭浜嬩欢', + 'Conditional Intermediate Catch Event': '鏉′欢涓棿鎹曡幏浜嬩欢', + 'Link Intermediate Catch Event': '閾炬帴涓棿鎹曡幏浜嬩欢', + 'Link Intermediate Throw Event': '閾炬帴涓棿鎶涘嚭浜嬩欢', + 'Compensation Intermediate Throw Event': '琛ュ伩涓棿鎶涘嚭浜嬩欢', + 'Signal Intermediate Catch Event': '淇″彿涓棿鎹曡幏浜嬩欢', + 'Signal Intermediate Throw Event': '淇″彿涓棿鎶涘嚭浜嬩欢', + 'Message End Event': '娑堟伅缁撴潫浜嬩欢', + 'Escalation End Event': '瀹氭椂缁撴潫浜嬩欢', + 'Error End Event': '閿欒缁撴潫浜嬩欢', + 'Cancel End Event': '鍙栨秷缁撴潫浜嬩欢', + 'Compensation End Event': '琛ュ伩缁撴潫浜嬩欢', + 'Signal End Event': '淇″彿缁撴潫浜嬩欢', + 'Terminate End Event': '缁堟缁撴潫浜嬩欢', + 'Message Boundary Event': '娑堟伅杈圭晫浜嬩欢', + 'Message Boundary Event (non-interrupting)': '娑堟伅杈圭晫浜嬩欢锛堥潪涓柇锛�', + 'Timer Boundary Event': '瀹氭椂杈圭晫浜嬩欢', + 'Timer Boundary Event (non-interrupting)': '瀹氭椂杈圭晫浜嬩欢锛堥潪涓柇锛�', + 'Escalation Boundary Event': '鍗囩骇杈圭晫浜嬩欢', + 'Escalation Boundary Event (non-interrupting)': '鍗囩骇杈圭晫浜嬩欢锛堥潪涓柇锛�', + 'Conditional Boundary Event': '鏉′欢杈圭晫浜嬩欢', + 'Conditional Boundary Event (non-interrupting)': '鏉′欢杈圭晫浜嬩欢锛堥潪涓柇锛�', + 'Error Boundary Event': '閿欒杈圭晫浜嬩欢', + 'Cancel Boundary Event': '鍙栨秷杈圭晫浜嬩欢', + 'Signal Boundary Event': '淇″彿杈圭晫浜嬩欢', + 'Signal Boundary Event (non-interrupting)': '淇″彿杈圭晫浜嬩欢锛堥潪涓柇锛�', + 'Compensation Boundary Event': '琛ュ伩杈圭晫浜嬩欢', + 'Exclusive Gateway': '浜掓枼缃戝叧', + 'Parallel Gateway': '骞惰缃戝叧', + 'Inclusive Gateway': '鐩稿缃戝叧', + 'Complex Gateway': '澶嶆潅缃戝叧', + 'Event based Gateway': '浜嬩欢缃戝叧', + Transaction: '杞繍', + 'Sub Process': '瀛愭祦绋�', + 'Event Sub Process': '浜嬩欢瀛愭祦绋�', + 'Collapsed Pool': '鎶樺彔姹�', + 'Expanded Pool': '灞曞紑姹�', + + // Errors + 'no parent for {element} in {parent}': '鍦▄parent}閲岋紝{element}娌℃湁鐖剁被', + 'no shape type specified': '娌℃湁鎸囧畾鐨勫舰鐘剁被鍨�', + 'flow elements must be children of pools/participants': '娴佸厓绱犲繀椤绘槸姹�/鍙備笌鑰呯殑瀛愮被', + 'out of bounds release': 'out of bounds release', + 'more than {count} child lanes': '瀛愰亾澶т簬{count} ', + 'element required': '鍏冪礌涓嶈兘涓虹┖', + 'diagram not part of bpmn:Definitions': '娴佺▼鍥句笉绗﹀悎bpmn瑙勮寖', + 'no diagram to display': '娌℃湁鍙睍绀虹殑娴佺▼鍥�', + 'no process or collaboration to display': '娌℃湁鍙睍绀虹殑娴佺▼/鍗忎綔', + 'element {element} referenced by {referenced}#{property} not yet drawn': '鐢眥referenced}#{property}寮曠敤鐨剓element}鍏冪礌浠嶆湭缁樺埗', + 'already rendered {element}': '{element} 宸茶娓叉煋', + 'failed to import {element}': '瀵煎叆{element}澶辫触', + // 灞炴�ч潰鏉跨殑鍙傛暟 + Id: '缂栧彿', + Name: '鍚嶇О', + General: '甯歌', + Details: '璇︽儏', + 'Message Name': '娑堟伅鍚嶇О', + Message: '娑堟伅', + Initiator: '鍒涘缓鑰�', + 'Asynchronous Continuations': '鎸佺画寮傛', + 'Asynchronous Before': '寮傛鍓�', + 'Asynchronous After': '寮傛鍚�', + 'Job Configuration': '宸ヤ綔閰嶇疆', + Exclusive: '鎺掗櫎', + 'Job Priority': '宸ヤ綔浼樺厛绾�', + 'Retry Time Cycle': '閲嶈瘯鏃堕棿鍛ㄦ湡', + Documentation: '鏂囨。', + 'Element Documentation': '鍏冪礌鏂囨。', + 'History Configuration': '鍘嗗彶閰嶇疆', + 'History Time To Live': '鍘嗗彶鐨勭敓瀛樻椂闂�', + Forms: '琛ㄥ崟', + 'Form Key': '琛ㄥ崟key', + 'Form Fields': '琛ㄥ崟瀛楁', + 'Business Key': '涓氬姟key', + 'Form Field': '琛ㄥ崟瀛楁', + ID: '缂栧彿', + Type: '绫诲瀷', + Label: '鍚嶇О', + 'Default Value': '榛樿鍊�', + 'Default Flow': '榛樿娴佽浆璺緞', + 'Conditional Flow': '鏉′欢娴佽浆璺緞', + 'Sequence Flow': '鏅�氭祦杞矾寰�', + Validation: '鏍¢獙', + 'Add Constraint': '娣诲姞绾︽潫', + Config: '閰嶇疆', + Properties: '灞炴��', + 'Add Property': '娣诲姞灞炴��', + Value: '鍊�', + Listeners: '鐩戝惉鍣�', + 'Execution Listener': '鎵ц鐩戝惉', + 'Event Type': '浜嬩欢绫诲瀷', + 'Listener Type': '鐩戝惉鍣ㄧ被鍨�', + 'Java Class': 'Java绫�', + Expression: '琛ㄨ揪寮�', + 'Must provide a value': '蹇呴』鎻愪緵涓�涓��', + 'Delegate Expression': '浠g悊琛ㄨ揪寮�', + Script: '鑴氭湰', + 'Script Format': '鑴氭湰鏍煎紡', + 'Script Type': '鑴氭湰绫诲瀷', + 'Inline Script': '鍐呰仈鑴氭湰', + 'External Script': '澶栭儴鑴氭湰', + Resource: '璧勬簮', + 'Field Injection': '瀛楁娉ㄥ叆', + Extensions: '鎵╁睍', + 'Input/Output': '杈撳叆/杈撳嚭', + 'Input Parameters': '杈撳叆鍙傛暟', + 'Output Parameters': '杈撳嚭鍙傛暟', + Parameters: '鍙傛暟', + 'Output Parameter': '杈撳嚭鍙傛暟', + 'Timer Definition Type': '瀹氭椂鍣ㄥ畾涔夌被鍨�', + 'Timer Definition': '瀹氭椂鍣ㄥ畾涔�', + Date: '鏃ユ湡', + Duration: '鎸佺画', + Cycle: '寰幆', + Signal: '淇″彿', + 'Signal Name': '淇″彿鍚嶇О', + Escalation: '鍗囩骇', + Error: '閿欒', + 'Link Name': '閾炬帴鍚嶇О', + Condition: '鏉′欢鍚嶇О', + 'Variable Name': '鍙橀噺鍚嶇О', + 'Variable Event': '鍙橀噺浜嬩欢', + 'Specify more than one variable change event as a comma separated list.': '澶氫釜鍙橀噺浜嬩欢浠ラ�楀彿闅斿紑', + 'Wait for Completion': '绛夊緟瀹屾垚', + 'Activity Ref': '娲诲姩鍙傝��', + 'Version Tag': '鐗堟湰鏍囩', + Executable: '鍙墽琛屾枃浠�', + 'External Task Configuration': '鎵╁睍浠诲姟閰嶇疆', + 'Task Priority': '浠诲姟浼樺厛绾�', + External: '澶栭儴', + Connector: '杩炴帴鍣�', + 'Must configure Connector': '蹇呴』閰嶇疆杩炴帴鍣�', + 'Connector Id': '杩炴帴鍣ㄧ紪鍙�', + Implementation: '瀹炵幇鏂瑰紡', + 'Field Injections': '瀛楁娉ㄥ叆', + Fields: '瀛楁', + 'Result Variable': '缁撴灉鍙橀噺', + Topic: '涓婚', + 'Configure Connector': '閰嶇疆杩炴帴鍣�', + 'Input Parameter': '杈撳叆鍙傛暟', + Assignee: '浠g悊浜�', + 'Candidate Users': '鍊欓�夌敤鎴�', + 'Candidate Groups': '鍊欓�夌粍', + 'Due Date': '鍒版湡鏃堕棿', + 'Follow Up Date': '璺熻釜鏃ユ湡', + Priority: '浼樺厛绾�', + 'The follow up date as an EL expression (e.g. ${someDate} or an ISO date (e.g. 2015-06-26T09:54:00)': + '璺熻釜鏃ユ湡蹇呴』绗﹀悎EL琛ㄨ揪寮忥紝濡傦細 ${someDate} ,鎴栬�呬竴涓狪SO鏍囧噯鏃ユ湡锛屽锛�2015-06-26T09:54:00', + 'The due date as an EL expression (e.g. ${someDate} or an ISO date (e.g. 2015-06-26T09:54:00)': + '璺熻釜鏃ユ湡蹇呴』绗﹀悎EL琛ㄨ揪寮忥紝濡傦細 ${someDate} ,鎴栬�呬竴涓狪SO鏍囧噯鏃ユ湡锛屽锛�2015-06-26T09:54:00', + Variables: '鍙橀噺', + 'Candidate Starter Configuration': '鍊欓�変汉璧峰姩鍣ㄩ厤缃�', + 'Candidate Starter Groups': '鍊欓�変汉璧峰姩鍣ㄧ粍', + 'This maps to the process definition key.': '杩欐槧灏勫埌娴佺▼瀹氫箟閿��', + 'Candidate Starter Users': '鍊欓�変汉璧峰姩鍣ㄧ殑鐢ㄦ埛', + 'Specify more than one user as a comma separated list.': '鎸囧畾澶氫釜鐢ㄦ埛浣滀负閫楀彿鍒嗛殧鐨勫垪琛ㄣ��', + 'Tasklist Configuration': 'Tasklist閰嶇疆', + Startable: '鍚姩', + 'Specify more than one group as a comma separated list.': '鎸囧畾澶氫釜缁勪綔涓洪�楀彿鍒嗛殧鐨勫垪琛ㄣ��' +}; diff --git a/ruoyi-ui/apps/web-antd/src/package/index.js b/ruoyi-ui/apps/web-antd/src/package/index.js new file mode 100644 index 0000000..62532f5 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/index.js @@ -0,0 +1,21 @@ +import MyProcessDesigner from './designer'; +import MyProcessPalette from './palette'; +import MyProcessPenal from './penal'; + +const components = [MyProcessDesigner, MyProcessPenal, MyProcessPalette]; + +const install = function(Vue) { + components.forEach(component => { + Vue.component(component.name, component); + }); +}; + +if (typeof window !== 'undefined' && window.Vue) { + install(window.Vue); +} + +export default { + version: '0.0.1', + install, + ...components +}; diff --git a/ruoyi-ui/apps/web-antd/src/package/palette/ProcessPalette.vue b/ruoyi-ui/apps/web-antd/src/package/palette/ProcessPalette.vue new file mode 100644 index 0000000..5fa1415 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/palette/ProcessPalette.vue @@ -0,0 +1,106 @@ +<template> + <div class="my-process-palette"> + <p>绠�鏄損alette</p> + <el-collapse> + <el-collapse-item title="浠诲姟" name="1"> + <!-- 鍙互绠�鍖栥�傘�傘�� --> + <div class="custom-button" @click="createElement($event, 'Task')" @mousedown="createElement($event, 'Task')"> + 浠诲姟 + </div> + <div class="custom-button" @click="createElement($event, 'UserTask')" @mousedown="createElement($event, 'UserTask')"> + 鐢ㄦ埛浠诲姟 + </div> + <div class="custom-button" @click="createElement($event, 'SendTask')" @mousedown="createElement($event, 'SendTask')"> + 鍙戦�佷换鍔� + </div> + <div class="custom-button" @click="createElement($event, 'ReceiveTask')" @mousedown="createElement($event, 'ReceiveTask')"> + 鎺ユ敹浠诲姟 + </div> + <div class="custom-button" @click="createElement($event, 'ScriptTask')" @mousedown="createElement($event, 'ScriptTask')"> + 鑴氭湰浠诲姟 + </div> + <div class="custom-button" @click="createElement($event, 'ServiceTask')" @mousedown="createElement($event, 'ServiceTask')"> + 鏈嶅姟浠诲姟 + </div> + </el-collapse-item> + <el-collapse-item title="缃戝叧" name="2"> + <div class="custom-button" @click="createElement($event, 'Gateway')" @mousedown="createElement($event, 'Gateway')"> + 缃戝叧 + </div> + </el-collapse-item> + <el-collapse-item title="寮�濮�" name="3"> + <div class="custom-button" @click="createElement($event, 'StartEvent')" @mousedown="createElement($event, 'StartEvent')"> + 寮�濮� + </div> + </el-collapse-item> + <el-collapse-item title="缁撴潫" name="4"> + <div class="custom-button" @click="createElement($event, 'EndEvent')" @mousedown="createElement($event, 'EndEvent')"> + 缁撴潫 + </div> + </el-collapse-item> + <el-collapse-item title="宸ュ叿" name="5"> + <div class="custom-button" @click="startTool($event, 'handTool')" @mousedown="startTool($event, 'handTool')"> + 鎵嬪瀷宸ュ叿 + </div> + <div class="custom-button" @click="startTool($event, 'lassoTool')" @mousedown="startTool($event, 'lassoTool')"> + 妗嗛�夊伐鍏� + </div> + <div class="custom-button" @click="startTool($event, 'connectTool')" @mousedown="startTool($event, 'connectTool')"> + 杩炵嚎宸ュ叿 + </div> + </el-collapse-item> + </el-collapse> + </div> +</template> + +<script> +import { assign } from 'min-dash'; + +export default { + name: 'MyProcessPalette', + data() { + return {}; + }, + mounted() {}, + methods: { + createElement(event, type, options = {}) { + const ElementFactory = window.bpmnInstances.elementFactory; + const create = window.bpmnInstances.modeler.get('create'); + const shape = ElementFactory.createShape(assign({ type: `bpmn:${type}` }, options)); + if (options) { + shape.businessObject.di.isExpanded = options.isExpanded; + } + create.start(event, shape); + }, + startTool(event, type) { + if (type === 'handTool') { + window.bpmnInstances.modeler.get('handTool').activateHand(event); + } + if (type === 'lassoTool') { + window.bpmnInstances.modeler.get('lassoTool').activateSelection(event); + } + if (type === 'connectTool') { + window.bpmnInstances.modeler.get('globalConnect').toggle(event); + } + } + } +}; +</script> + +<style scoped lang="scss"> +.my-process-palette { + box-sizing: border-box; + padding: 8px; + .custom-button { + box-sizing: border-box; + padding: 4px 8px; + border-radius: 4px; + border: 1px solid rgba(24, 144, 255, 0.8); + cursor: pointer; + margin-bottom: 8px; + &:first-child { + margin-top: 8px; + } + } +} +</style> diff --git a/ruoyi-ui/apps/web-antd/src/package/palette/index.js b/ruoyi-ui/apps/web-antd/src/package/palette/index.js new file mode 100644 index 0000000..1e94b22 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/palette/index.js @@ -0,0 +1,7 @@ +import MyPropertiesPalette from './ProcessPalette.vue'; + +MyPropertiesPalette.install = function(Vue) { + Vue.component(MyPropertiesPalette.name, MyPropertiesPalette); +}; + +export default MyPropertiesPalette; diff --git a/ruoyi-ui/apps/web-antd/src/package/penal/PropertiesPanel.vue b/ruoyi-ui/apps/web-antd/src/package/penal/PropertiesPanel.vue new file mode 100644 index 0000000..8e04309 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/penal/PropertiesPanel.vue @@ -0,0 +1,216 @@ +<template> + <div class="process-panel__container" :style="{ width: `${this.width}px` }"> + <el-collapse v-model="activeTab"> + <el-collapse-item name="base"> + <template #title> + <div class="panel-tab__title"><el-icon><info-filled /></el-icon>甯歌</div> + </template> + <element-base-info :id-edit-disabled="idEditDisabled" :business-object="elementBusinessObject" :type="elementType" /> + </el-collapse-item> + <el-collapse-item name="condition" v-if="elementType === 'Process'" key="message"> + <template #title> + <div class="panel-tab__title"><el-icon><comment /></el-icon>娑堟伅涓庝俊鍙�</div> + </template> + <signal-and-massage /> + </el-collapse-item> + <el-collapse-item name="condition" v-if="conditionFormVisible" key="condition"> + <template #title> + <div class="panel-tab__title"><el-icon><promotion /></el-icon>娴佽浆鏉′欢</div> + </template> + <flow-condition :business-object="elementBusinessObject" :type="elementType" /> + </el-collapse-item> + <el-collapse-item name="condition" v-if="formVisible" key="form"> + <template #title> + <div class="panel-tab__title"><el-icon><list /></el-icon>琛ㄥ崟</div> + </template> + <element-form :id="elementId" :type="elementType" /> + </el-collapse-item> + <el-collapse-item name="task" v-if="elementType.indexOf('Task') !== -1" key="task"> + <template #title> + <div class="panel-tab__title"><el-icon><checked /></el-icon>浠诲姟</div> + </template> + <element-task :id="elementId" :type="elementType" /> + </el-collapse-item> + <el-collapse-item name="multiInstance" v-if="elementType.indexOf('Task') !== -1" key="multiInstance"> + <template #title> + <div class="panel-tab__title"><el-icon><help-filled /></el-icon>澶氬疄渚�</div> + </template> + <element-multi-instance :business-object="elementBusinessObject" :type="elementType" /> + </el-collapse-item> + <el-collapse-item name="taskListeners" v-if="elementType === 'UserTask'" key="taskListeners"> + <template #title> + <div class="panel-tab__title"><el-icon><bell-filled /></el-icon>浠诲姟鐩戝惉鍣�</div> + </template> + <user-task-listeners :id="elementId" :type="elementType" /> + </el-collapse-item> + <el-collapse-item name="listeners" key="listeners"> + <template #title> + <div class="panel-tab__title"><el-icon><bell-filled /></el-icon>鎵ц鐩戝惉鍣�</div> + </template> + <element-listeners :id="elementId" :type="elementType" /> + </el-collapse-item> + <el-collapse-item name="extensions" key="extensions"> + <template #title> + <div class="panel-tab__title"><el-icon><circle-plus /></el-icon>鎵╁睍灞炴��</div> + </template> + <element-properties :id="elementId" :type="elementType" /> + </el-collapse-item> + <el-collapse-item name="other" key="other"> + <template #title> + + <div class="panel-tab__title"><el-icon><promotion /></el-icon>鍏朵粬</div> + </template> + <element-other-config :id="elementId" /> + </el-collapse-item> + </el-collapse> + </div> +</template> +<script> +import ElementBaseInfo from "./base/ElementBaseInfo.vue"; +import ElementOtherConfig from "./other/ElementOtherConfig.vue"; +import ElementTask from "./task/ElementTask.vue"; +import ElementMultiInstance from "./multi-instance/ElementMultiInstance.vue"; +import FlowCondition from "./flow-condition/FlowCondition.vue"; +import SignalAndMassage from "./signal-message/SignalAndMessage.vue"; +import ElementListeners from "./listeners/ElementListeners.vue"; +import ElementProperties from "./properties/ElementProperties.vue"; +import ElementForm from "./form/ElementForm.vue"; +import UserTaskListeners from "./listeners/UserTaskListeners.vue"; +import Log from "../Log"; +/** + * 渚ц竟鏍� + * @Author MiyueFE +import ElementProperties from "./properties/ElementProperties"; +import ElementForm from "./form/ElementForm"; +import UserTaskListeners from "./listeners/UserTaskListeners"; +import Log from "../Log"; +/** + * 渚ц竟鏍� + * @Author MiyueFE + * @Home https://github.com/miyuesc + * @Date 2021骞�3鏈�31鏃�18:57:51 + */ +export default { + name: "MyPropertiesPanel", + components: { + UserTaskListeners, + ElementForm, + ElementProperties, + ElementListeners, + SignalAndMassage, + FlowCondition, + ElementMultiInstance, + ElementTask, + ElementOtherConfig, + ElementBaseInfo + }, + componentName: "MyPropertiesPanel", + props: { + bpmnModeler: Object, + prefix: { + type: String, + default: "camunda" + }, + width: { + type: Number, + default: 480 + }, + idEditDisabled: { + type: Boolean, + default: false + } + }, + provide() { + return { + prefix: this.prefix, + width: this.width + }; + }, + data() { + return { + activeTab: "base", + elementId: "", + elementType: "", + elementBusinessObject: {}, // 鍏冪礌 businessObject 闀滃儚锛屾彁渚涚粰闇�瑕佸仛鍒ゆ柇鐨勭粍浠朵娇鐢� + conditionFormVisible: false, // 娴佽浆鏉′欢璁剧疆 + formVisible: false // 琛ㄥ崟閰嶇疆 + }; + }, + watch: { + elementId: { + handler() { + this.activeTab = "base"; + } + } + }, + created() { + this.initModels(); + }, + methods: { + initModels() { + // 鍒濆鍖� modeler 浠ュ強鍏朵粬 moddle + if (!this.bpmnModeler) { + // 閬垮厤鍔犺浇鏃� 娴佺▼鍥� 骞舵湭鍔犺浇瀹屾垚 + this.timer = setTimeout(() => this.initModels(), 10); + return; + } + if (this.timer) clearTimeout(this.timer); + window.bpmnInstances = { + modeler: this.bpmnModeler, + modeling: this.bpmnModeler.get("modeling"), + moddle: this.bpmnModeler.get("moddle"), + eventBus: this.bpmnModeler.get("eventBus"), + bpmnFactory: this.bpmnModeler.get("bpmnFactory"), + elementFactory: this.bpmnModeler.get("elementFactory"), + elementRegistry: this.bpmnModeler.get("elementRegistry"), + replace: this.bpmnModeler.get("replace"), + selection: this.bpmnModeler.get("selection") + }; + this.getActiveElement(); + }, + getActiveElement() { + // 鍒濆绗竴涓�変腑鍏冪礌 bpmn:Process + this.initFormOnChanged(null); + this.bpmnModeler.on("import.done", e => { + this.initFormOnChanged(null); + }); + // 鐩戝惉閫夋嫨浜嬩欢锛屼慨鏀瑰綋鍓嶆縺娲荤殑鍏冪礌浠ュ強琛ㄥ崟 + this.bpmnModeler.on("selection.changed", ({ newSelection }) => { + this.initFormOnChanged(newSelection[0] || null); + }); + this.bpmnModeler.on("element.changed", ({ element }) => { + // 淇濊瘉 淇敼 "榛樿娴佽浆璺緞" 绫讳技闇�瑕佷慨鏀瑰涓厓绱犵殑浜嬩欢鍙戠敓鐨勬椂鍊欙紝鏇存柊琛ㄥ崟鐨勫厓绱犱笌鍘熼�変腑鍏冪礌涓嶄竴鑷淬�� + if (element && element.id === this.elementId) { + this.initFormOnChanged(element); + } + }); + }, + // 鍒濆鍖栨暟鎹� + initFormOnChanged(element) { + let activatedElement = element; + if (!activatedElement) { + activatedElement = + window.bpmnInstances.elementRegistry.find(el => el.type === "bpmn:Process") ?? + window.bpmnInstances.elementRegistry.find(el => el.type === "bpmn:Collaboration"); + } + if (!activatedElement) return; + Log.printBack(`select element changed: id: ${activatedElement.id} , type: ${activatedElement.businessObject.$type}`); + Log.prettyInfo("businessObject", activatedElement.businessObject); + window.bpmnInstances.bpmnElement = activatedElement; + this.bpmnElement = activatedElement; + this.elementId = activatedElement.id; + this.elementType = activatedElement.type.split(":")[1] || ""; + this.elementBusinessObject = JSON.parse(JSON.stringify(activatedElement.businessObject)); + this.conditionFormVisible = !!( + this.elementType === "SequenceFlow" && + activatedElement.source && + activatedElement.source.type.indexOf("StartEvent") === -1 + ); + this.formVisible = this.elementType === "UserTask" || this.elementType === "StartEvent"; + }, + beforeUnmount() { + window.bpmnInstances = null; + } + } +}; +</script> diff --git a/ruoyi-ui/apps/web-antd/src/package/penal/base/ElementBaseInfo.vue b/ruoyi-ui/apps/web-antd/src/package/penal/base/ElementBaseInfo.vue new file mode 100644 index 0000000..a6d1f9a --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/penal/base/ElementBaseInfo.vue @@ -0,0 +1,80 @@ +<template> + <div class="panel-tab__content"> + <el-form size="small" label-width="90px" @submit.prevent> + <el-form-item label="ID"> + <el-input v-model="elementBaseInfo.id" :disabled="idEditDisabled" clearable @change="updateBaseInfo('id')" /> + </el-form-item> + <el-form-item label="鍚嶇О"> + <el-input v-model="elementBaseInfo.name" clearable @change="updateBaseInfo('name')" /> + </el-form-item> + <!--娴佺▼鐨勫熀纭�灞炴��--> + <template v-if="elementBaseInfo.$type === 'bpmn:Process'"> + <el-form-item label="鐗堟湰鏍囩"> + <el-input v-model="elementBaseInfo.versionTag" clearable @change="updateBaseInfo('versionTag')" /> + </el-form-item> + <el-form-item label="鍙墽琛�"> + <el-switch v-model="elementBaseInfo.isExecutable" active-text="鏄�" inactive-text="鍚�" @change="updateBaseInfo('isExecutable')" /> + </el-form-item> + </template> + <el-form-item v-if="elementBaseInfo.$type === 'bpmn:SubProcess'" label="鐘舵��"> + <el-switch v-model="elementBaseInfo.isExpanded" active-text="灞曞紑" inactive-text="鎶樺彔" @change="updateBaseInfo('isExpanded')" /> + </el-form-item> + </el-form> + </div> +</template> +<script> +export default { + name: "ElementBaseInfo", + props: { + businessObject: Object, + type: String, + idEditDisabled: { + type: Boolean, + default: true + } + }, + data() { + return { + elementBaseInfo: {} + }; + }, + watch: { + businessObject: { + immediate: false, + handler: function(val) { + if (val) { + this.$nextTick(() => this.resetBaseInfo()); + } + } + } + }, + methods: { + resetBaseInfo() { + this.bpmnElement = window?.bpmnInstances?.bpmnElement || {}; + this.elementBaseInfo = JSON.parse(JSON.stringify(this.bpmnElement.businessObject)); + if (this.elementBaseInfo && this.elementBaseInfo.$type === "bpmn:SubProcess") { + this.elementBaseInfo["isExpanded"] = this.elementBaseInfo.di?.isExpanded + } + }, + updateBaseInfo(key) { + if (key === "id") { + window.bpmnInstances.modeling.updateProperties(this.bpmnElement, { + id: this.elementBaseInfo[key], + di: { id: `${this.elementBaseInfo[key]}_di` } + }); + return; + } + if (key === "isExpanded") { + window?.bpmnInstances?.modeling.toggleCollapse(this.bpmnElement); + return; + } + const attrObj = Object.create(null); + attrObj[key] = this.elementBaseInfo[key]; + window.bpmnInstances.modeling.updateProperties(this.bpmnElement, attrObj); + } + }, + beforeUnmount() { + this.bpmnElement = null; + } +}; +</script> diff --git a/ruoyi-ui/apps/web-antd/src/package/penal/flow-condition/FlowCondition.vue b/ruoyi-ui/apps/web-antd/src/package/penal/flow-condition/FlowCondition.vue new file mode 100644 index 0000000..d06eeb6 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/penal/flow-condition/FlowCondition.vue @@ -0,0 +1,142 @@ +<template> + <div class="panel-tab__content"> + <el-form :model="flowConditionForm" label-width="90px" size="small" @submit.prevent> + <el-form-item label="娴佽浆绫诲瀷"> + <el-select v-model="flowConditionForm.type" @change="updateFlowType"> + <el-option label="鏅�氭祦杞矾寰�" value="normal" /> + <el-option label="榛樿娴佽浆璺緞" value="default" /> + <el-option label="鏉′欢娴佽浆璺緞" value="condition" /> + </el-select> + </el-form-item> + <el-form-item label="鏉′欢鏍煎紡" v-if="flowConditionForm.type === 'condition'" key="condition"> + <el-select v-model="flowConditionForm.conditionType"> + <el-option label="琛ㄨ揪寮�" value="expression" /> + <el-option label="鑴氭湰" value="script" /> + </el-select> + </el-form-item> + <el-form-item label="琛ㄨ揪寮�" v-if="flowConditionForm.conditionType && flowConditionForm.conditionType === 'expression'" key="express"> + <el-input v-model="flowConditionForm.body" clearable @change="updateFlowCondition" /> + </el-form-item> + <template v-if="flowConditionForm.conditionType && flowConditionForm.conditionType === 'script'"> + <el-form-item label="鑴氭湰璇█" key="language"> + <el-input v-model="flowConditionForm.language" clearable @change="updateFlowCondition" /> + </el-form-item> + <el-form-item label="鑴氭湰绫诲瀷" key="scriptType"> + <el-select v-model="flowConditionForm.scriptType"> + <el-option label="鍐呰仈鑴氭湰" value="inlineScript" /> + <el-option label="澶栭儴鑴氭湰" value="externalScript" /> + </el-select> + </el-form-item> + <el-form-item label="鑴氭湰" v-if="flowConditionForm.scriptType === 'inlineScript'" key="body"> + <el-input v-model="flowConditionForm.body" type="textarea" clearable @change="updateFlowCondition" /> + </el-form-item> + <el-form-item label="璧勬簮鍦板潃" v-if="flowConditionForm.scriptType === 'externalScript'" key="resource"> + <el-input v-model="flowConditionForm.resource" clearable @change="updateFlowCondition" /> + </el-form-item> + </template> + </el-form> + </div> +</template> + +<script> +export default { + name: 'FlowCondition', + props: { + businessObject: Object, + type: String + }, + data() { + return { + flowConditionForm: {} + }; + }, + watch: { + businessObject: { + immediate: true, + handler() { + this.$nextTick(() => this.resetFlowCondition()); + } + } + }, + methods: { + resetFlowCondition() { + this.bpmnElement = window.bpmnInstances.bpmnElement; + this.bpmnElementSource = this.bpmnElement.source; + this.bpmnElementSourceRef = this.bpmnElement.businessObject.sourceRef; + if (this.bpmnElementSourceRef && this.bpmnElementSourceRef.default && this.bpmnElementSourceRef.default.id === this.bpmnElement.id) { + // 榛樿 + this.flowConditionForm = { type: 'default' }; + } else if (!this.bpmnElement.businessObject.conditionExpression) { + // 鏅�� + this.flowConditionForm = { type: 'normal' }; + } else { + // 甯︽潯浠� + const conditionExpression = this.bpmnElement.businessObject.conditionExpression; + this.flowConditionForm = { ...conditionExpression, type: 'condition' }; + // resource 鍙洿鎺ユ爣璇� 鏄惁鏄閮ㄨ祫婧愯剼鏈� + if (this.flowConditionForm.resource) { + this.flowConditionForm['conditionType'] = 'script' + this.flowConditionForm['scriptType'] = 'externalScript' + return; + } + if (conditionExpression.language) { + this.flowConditionForm['conditionType'] = 'script' + this.flowConditionForm['scriptType'] = 'inlineScript' + return; + } + this.flowConditionForm['conditionType'] = 'expression' + } + }, + updateFlowType(flowType) { + // 姝e父鏉′欢绫� + if (flowType === 'condition') { + this.flowConditionRef = window.bpmnInstances.moddle.create('bpmn:FormalExpression'); + window.bpmnInstances.modeling.updateProperties(this.bpmnElement, { + conditionExpression: this.flowConditionRef + }); + return; + } + // 榛樿璺緞 + if (flowType === 'default') { + window.bpmnInstances.modeling.updateProperties(this.bpmnElement, { + conditionExpression: null + }); + window.bpmnInstances.modeling.updateProperties(this.bpmnElementSource, { + default: this.bpmnElement + }); + return; + } + // 姝e父璺緞锛屽鏋滄潵婧愯妭鐐圭殑榛樿璺緞鏄綋鍓嶈繛绾挎椂锛屾竻闄ょ埗鍏冪礌鐨勯粯璁よ矾寰勯厤缃� + if (this.bpmnElementSourceRef.default && this.bpmnElementSourceRef.default.id === this.bpmnElement.id) { + window.bpmnInstances.modeling.updateProperties(this.bpmnElementSource, { + default: null + }); + } + window.bpmnInstances.modeling.updateProperties(this.bpmnElement, { + conditionExpression: null + }); + }, + updateFlowCondition() { + const { conditionType, scriptType, body, resource, language } = this.flowConditionForm; + let condition; + if (conditionType === 'expression') { + condition = window.bpmnInstances.moddle.create('bpmn:FormalExpression', { body }); + } else { + if (scriptType === 'inlineScript') { + condition = window.bpmnInstances.moddle.create('bpmn:FormalExpression', { body, language }); + this.flowConditionForm['resource'] = '' + } else { + this.flowConditionForm['body'] = '' + condition = window.bpmnInstances.moddle.create('bpmn:FormalExpression', { resource, language }); + } + } + window.bpmnInstances.modeling.updateProperties(this.bpmnElement, { conditionExpression: condition }); + } + }, + beforeUnmount() { + this.bpmnElement = null; + this.bpmnElementSource = null; + this.bpmnElementSourceRef = null; + } +}; +</script> diff --git a/ruoyi-ui/apps/web-antd/src/package/penal/form/ElementForm.vue b/ruoyi-ui/apps/web-antd/src/package/penal/form/ElementForm.vue new file mode 100644 index 0000000..33628bf --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/penal/form/ElementForm.vue @@ -0,0 +1,417 @@ +<template> + <div class="panel-tab__content"> + <el-form size="small" label-width="80px" @submit.prevent> + <el-form-item label="琛ㄥ崟" prop="formKey"> + <el-select v-model="formKey" placeholder="璇烽�夋嫨琛ㄥ崟" @change="updateElementFormKey" clearable> + <el-option v-for="item in formOptions" :key="item.formId" :label="item.formName" :value="`key_${item.formId}`" /> + </el-select> + </el-form-item> + <el-form-item prop="localScope"> + <template #label> + <div class="flex align-center"> + <el-tooltip content="鑻ヤ负鑺傜偣琛ㄥ崟锛屽垯琛ㄥ崟淇℃伅浠呭湪姝よ妭鐐瑰彲鐢紝榛樿涓哄叏灞�琛ㄥ崟锛岃〃鍗曚俊鎭湪鏁翠釜娴佺▼瀹炰緥涓彲鐢�" placement="top-start"> + <el-icon><InfoFilled /></el-icon> + </el-tooltip> + <span>鑺傜偣琛ㄥ崟</span> + </div> + </template> + <el-switch + :disabled="type === 'StartEvent'" + v-model="localScope" + active-text="鏄�" + inactive-text="鍚�" + @change="updateElementFormScope()" + ></el-switch> + </el-form-item> + <!-- <el-form-item label="琛ㄥ崟鏍囪瘑">--> + <!-- <el-input v-model="formKey" clearable @change="updateElementFormKey" />--> + <!-- </el-form-item>--> + <!-- <el-form-item label="涓氬姟鏍囪瘑">--> + <!-- <el-select v-model="businessKey" @change="updateElementBusinessKey">--> + <!-- <el-option v-for="i in fieldList" :key="i.id" :value="i.id" :label="i.label" />--> + <!-- <el-option label="鏃�" value="" />--> + <!-- </el-select>--> + <!-- </el-form-item>--> + </el-form> + + <!--瀛楁鍒楄〃--> + <!-- <div class="element-property list-property">--> + <!-- <el-divider><el-icon><coin /></el-icon> 琛ㄥ崟瀛楁</el-divider>--> + <!-- <el-table :data="fieldList" size="small" max-height="240" border fit>--> + <!-- <el-table-column label="搴忓彿" type="index" width="50px" />--> + <!-- <el-table-column label="瀛楁鍚嶇О" prop="label" min-width="80px" show-overflow-tooltip />--> + <!-- <el-table-column label="瀛楁绫诲瀷" prop="type" min-width="80px" :formatter="row => fieldType[row.type] || row.type" show-overflow-tooltip />--> + <!-- <el-table-column label="榛樿鍊�" prop="defaultValue" min-width="80px" show-overflow-tooltip />--> + <!-- <el-table-column label="鎿嶄綔" width="90px">--> + <!-- <template v-slot="{ row, $index }">--> + <!-- <el-button link type="" @click="openFieldForm(row, $index)">缂栬緫</el-button>--> + <!-- <el-divider direction="vertical" />--> + <!-- <el-button link type="" style="color: #ff4d4f" @click="removeField(row, $index)">绉婚櫎</el-button>--> + <!-- </template>--> + <!-- </el-table-column>--> + <!-- </el-table>--> + <!-- </div>--> + <!-- <div class="element-drawer__button">--> + <!-- <el-button size="small" type="primary" :icon="Plus" @click="openFieldForm(null, -1)">娣诲姞瀛楁</el-button>--> + <!-- </div>--> + + <!--瀛楁閰嶇疆渚ц竟鏍�--> + <!-- <el-drawer v-model="fieldModelVisible" title="瀛楁閰嶇疆" :size="`${width}px`" append-to-body destroy-on-close>--> + <!-- <el-form :model="formFieldForm" label-width="90px" size="small" @submit.prevent>--> + <!-- <el-form-item label="瀛楁ID">--> + <!-- <el-input v-model="formFieldForm.id" clearable />--> + <!-- </el-form-item>--> + <!-- <el-form-item label="绫诲瀷">--> + <!-- <el-select v-model="formFieldForm.typeType" placeholder="璇烽�夋嫨瀛楁绫诲瀷" clearable @change="changeFieldTypeType">--> + <!-- <el-option v-for="(value, key) of fieldType" :key="key" :label="value" :value="key" />--> + <!-- </el-select>--> + <!-- </el-form-item>--> + <!-- <el-form-item v-if="formFieldForm.typeType === 'custom'" label="绫诲瀷鍚嶇О">--> + <!-- <el-input v-model="formFieldForm.type" clearable />--> + <!-- </el-form-item>--> + <!-- <el-form-item label="鍚嶇О">--> + <!-- <el-input v-model="formFieldForm.label" clearable />--> + <!-- </el-form-item>--> + <!-- <el-form-item v-if="formFieldForm.typeType === 'date'" label="鏃堕棿鏍煎紡">--> + <!-- <el-input v-model="formFieldForm.datePattern" clearable />--> + <!-- </el-form-item>--> + <!-- <el-form-item label="榛樿鍊�">--> + <!-- <el-input v-model="formFieldForm.defaultValue" clearable />--> + <!-- </el-form-item>--> + <!-- </el-form>--> + + <!-- 鏋氫妇鍊艰缃� --> + <!-- <template v-if="formFieldForm.type === 'enum'">--> + <!-- <el-divider key="enum-divider" />--> + <!-- <p key="enum-title" class="listener-filed__title">--> + <!-- <span>--> + <!-- <el-icon>--> + <!-- <menu />--> + <!-- </el-icon>--> + <!-- 鏋氫妇鍊煎垪琛細--> + <!-- </span>--> + <!-- <el-button size="small" type="primary" @click="openFieldOptionForm(null, -1, 'enum')">娣诲姞鏋氫妇鍊�</el-button>--> + <!-- </p>--> + <!-- <el-table key="enum-table" :data="fieldEnumList" size="small" max-height="240" border fit>--> + <!-- <el-table-column label="搴忓彿" width="50px" type="index" />--> + <!-- <el-table-column label="鏋氫妇鍊肩紪鍙�" prop="id" min-width="100px" show-overflow-tooltip />--> + <!-- <el-table-column label="鏋氫妇鍊煎悕绉�" prop="name" min-width="100px" show-overflow-tooltip />--> + <!-- <el-table-column label="鎿嶄綔" width="90px">--> + <!-- <template v-slot="{ row, $index }">--> + <!-- <el-button link type="" @click="openFieldOptionForm(row, $index, 'enum')">缂栬緫</el-button>--> + <!-- <el-divider direction="vertical" />--> + <!-- <el-button link type="" style="color: #ff4d4f" @click="removeFieldOptionItem(row, $index, 'enum')">绉婚櫎</el-button>--> + <!-- </template>--> + <!-- </el-table-column>--> + <!-- </el-table>--> + <!-- </template>--> + + <!-- 鏍¢獙瑙勫垯 --> + <!-- <el-divider key="validation-divider" />--> + <!-- <p key="validation-title" class="listener-filed__title">--> + <!-- <span><el-icon><menu /></el-icon>绾︽潫鏉′欢鍒楄〃锛�</span>--> + <!-- <el-button size="small" type="primary" @click="openFieldOptionForm(null, -1, 'constraint')">娣诲姞绾︽潫</el-button>--> + <!-- </p>--> + <!-- <el-table key="validation-table" :data="fieldConstraintsList" size="small" max-height="240" border fit>--> + <!-- <el-table-column label="搴忓彿" width="50px" type="index" />--> + <!-- <el-table-column label="绾︽潫鍚嶇О" prop="name" min-width="100px" show-overflow-tooltip />--> + <!-- <el-table-column label="绾︽潫閰嶇疆" prop="config" min-width="100px" show-overflow-tooltip />--> + <!-- <el-table-column label="鎿嶄綔" width="90px">--> + <!-- <template v-slot="{ row, $index }">--> + <!-- <el-button link type="" @click="openFieldOptionForm(row, $index, 'constraint')">缂栬緫</el-button>--> + <!-- <el-divider direction="vertical" />--> + <!-- <el-button link type="" style="color: #ff4d4f" @click="removeFieldOptionItem(row, $index, 'constraint')">绉婚櫎</el-button>--> + <!-- </template>--> + <!-- </el-table-column>--> + <!-- </el-table>--> + + <!-- 琛ㄥ崟灞炴�� --> + <!-- <el-divider key="property-divider" />--> + <!-- <p key="property-title" class="listener-filed__title">--> + <!-- <span><el-icon><menu /></el-icon>瀛楁灞炴�у垪琛細</span>--> + <!-- <el-button size="small" type="primary" @click="openFieldOptionForm(null, -1, 'property')">娣诲姞灞炴��</el-button>--> + <!-- </p>--> + <!-- <el-table key="property-table" :data="fieldPropertiesList" size="small" max-height="240" border fit>--> + <!-- <el-table-column label="搴忓彿" width="50px" type="index" />--> + <!-- <el-table-column label="灞炴�х紪鍙�" prop="id" min-width="100px" show-overflow-tooltip />--> + <!-- <el-table-column label="灞炴�у��" prop="value" min-width="100px" show-overflow-tooltip />--> + <!-- <el-table-column label="鎿嶄綔" width="90px">--> + <!-- <template v-slot="{ row, $index }">--> + <!-- <el-button link type="" @click="openFieldOptionForm(row, $index, 'property')">缂栬緫</el-button>--> + <!-- <el-divider direction="vertical" />--> + <!-- <el-button link type="" style="color: #ff4d4f" @click="removeFieldOptionItem(row, $index, 'property')">绉婚櫎</el-button>--> + <!-- </template>--> + <!-- </el-table-column>--> + <!-- </el-table>--> + + <!-- 搴曢儴鎸夐挳 --> + <!-- <div class="element-drawer__button">--> + <!-- <el-button size="small">鍙� 娑�</el-button>--> + <!-- <el-button size="small" type="primary" @click="saveField">淇� 瀛�</el-button>--> + <!-- </div>--> + <!-- </el-drawer>--> + + <el-dialog v-model="fieldOptionModelVisible" :title="optionModelTitle" width="600px" append-to-body destroy-on-close> + <el-form :model="fieldOptionForm" size="small" label-width="96px" @submit.prevent> + <el-form-item v-if="fieldOptionType !== 'constraint'" key="option-id" label="缂栧彿/ID"> + <el-input v-model="fieldOptionForm.id" clearable /> + </el-form-item> + <el-form-item v-if="fieldOptionType !== 'property'" key="option-name" label="鍚嶇О"> + <el-input v-model="fieldOptionForm.name" clearable /> + </el-form-item> + <el-form-item v-if="fieldOptionType === 'constraint'" key="option-config" label="閰嶇疆"> + <el-input v-model="fieldOptionForm.config" clearable /> + </el-form-item> + <el-form-item v-if="fieldOptionType === 'property'" key="option-value" label="鍊�"> + <el-input v-model="fieldOptionForm.value" clearable /> + </el-form-item> + </el-form> + <template v-slot:footer> + <el-button size="small" @click="fieldOptionModelVisible = false">鍙� 娑�</el-button> + <el-button size="small" type="primary" @click="saveFieldOption">纭� 瀹�</el-button> + </template> + </el-dialog> + </div> +</template> + +<script> +import { listForm } from "#/api/workflow/form"; + +export default { + name: 'ElementForm', + setup() { + return { + Plus + } + }, + props: { + id: String, + type: String + }, + inject: { + prefix: 'prefix', + width: 'width' + }, + data() { + return { + formKey: '', + formOptions: [], + localScope: false, + businessKey: '', + optionModelTitle: '', + fieldList: [], + formFieldForm: {}, + fieldType: { + long: '闀挎暣鍨�', + string: '瀛楃涓�', + boolean: '甯冨皵绫�', + date: '鏃ユ湡绫�', + enum: '鏋氫妇绫�', + custom: '鑷畾涔夌被鍨�' + }, + formFieldIndex: -1, // 缂栬緫涓殑瀛楁锛� -1 涓烘柊澧� + formFieldOptionIndex: -1, // 缂栬緫涓殑瀛楁閰嶇疆椤癸紝 -1 涓烘柊澧� + fieldModelVisible: false, + fieldOptionModelVisible: false, + fieldOptionForm: {}, // 褰撳墠婵�娲荤殑瀛楁閰嶇疆椤规暟鎹� + fieldOptionType: '', // 褰撳墠婵�娲荤殑瀛楁閰嶇疆椤瑰脊绐� 绫诲瀷 + fieldEnumList: [], // 鏋氫妇鍊煎垪琛� + fieldConstraintsList: [], // 绾︽潫鏉′欢鍒楄〃 + fieldPropertiesList: [] // 缁戝畾灞炴�у垪琛� + }; + }, + watch: { + id: { + immediate: true, + handler(val) { + val && val.length && this.$nextTick(() => this.resetFormList()); + } + } + }, + created() { + /** 鏌ヨ娴佺▼鍒嗙被鍒楄〃 */ + this.getFormList(); + }, + methods: { + /** 鏌ヨ琛ㄥ崟鍒楄〃 */ + getFormList() { + listForm().then(response => { + this.formOptions = response.rows; + } + ) + }, + resetFormList() { + this.bpmnELement = window.bpmnInstances.bpmnElement; + this.formKey = this.bpmnELement.businessObject.formKey; + this.localScope = this.bpmnELement.businessObject.localScope; + // 鑾峰彇鍏冪礌鎵╁睍灞炴�� 鎴栬�� 鍒涘缓鎵╁睍灞炴�� + this.elExtensionElements = + this.bpmnELement.businessObject.get('extensionElements') || window.bpmnInstances.moddle.create('bpmn:ExtensionElements', { values: [] }); + // 鑾峰彇鍏冪礌琛ㄥ崟閰嶇疆 鎴栬�� 鍒涘缓鏂扮殑琛ㄥ崟閰嶇疆 + // try { + // this.formData = + // this.elExtensionElements.values.filter(ex => ex.$type === `${this.prefix}:FormData`)[0] || + // window.bpmnInstances.moddle.create(`${this.prefix}:FormData`, { fields: [] }); + // } catch (error) { + // this.formData = {} + // console.log(error) + // } + + // 涓氬姟鏍囪瘑 businessKey锛� 缁戝畾鍦� formData 涓� + // this.businessKey = this.formData.businessKey; + + // 淇濈暀鍓╀綑鎵╁睍鍏冪礌锛屼究浜庡悗闈㈡洿鏂拌鍏冪礌瀵瑰簲灞炴�� + this.otherExtensions = this.elExtensionElements.values.filter(ex => ex.$type !== `${this.prefix}:FormData`); + + // 澶嶅埗鍘熷鍊硷紝濉厖琛ㄦ牸 + // this.fieldList = JSON.parse(JSON.stringify(this.formData.fields || [])); + + // 鏇存柊鍏冪礌鎵╁睍灞炴�э紝閬垮厤鍚庣画鎶ラ敊 + // this.updateElementExtensions(); + }, + updateElementFormKey() { + window.bpmnInstances.modeling.updateProperties(this.bpmnELement, { formKey: this.formKey }); + }, + updateElementFormScope() { + window.bpmnInstances.modeling.updateProperties(this.bpmnELement, { localScope: this.localScope }); + }, + updateElementBusinessKey() { + window.bpmnInstances.modeling.updateModdleProperties(this.bpmnELement, this.formData, { businessKey: this.businessKey }); + }, + // 鏍规嵁绫诲瀷璋冩暣瀛楁type + changeFieldTypeType(type) { + this.formFieldForm['type'] = type === 'custom' ? '' : type + }, + + // 鎵撳紑瀛楁璇︽儏渚ц竟鏍� + openFieldForm(field, index) { + this.formFieldIndex = index; + if (index !== -1) { + const FieldObject = this.formData.fields[index]; + this.formFieldForm = JSON.parse(JSON.stringify(field)); + // 璁剧疆鑷畾涔夌被鍨� + this.formFieldForm['typeType'] = !this.fieldType[field.type] ? 'custom' : field.type + // 鍒濆鍖栨灇涓惧�煎垪琛� + field.type === 'enum' && (this.fieldEnumList = JSON.parse(JSON.stringify(FieldObject?.values || []))); + // 鍒濆鍖栫害鏉熸潯浠跺垪琛� + this.fieldConstraintsList = JSON.parse(JSON.stringify(FieldObject?.validation?.constraints || [])); + // 鍒濆鍖栬嚜瀹氫箟灞炴�у垪琛� + this.fieldPropertiesList = JSON.parse(JSON.stringify(FieldObject?.properties?.values || [])); + } else { + this.formFieldForm = {}; + // 鍒濆鍖栨灇涓惧�煎垪琛� + this.fieldEnumList = []; + // 鍒濆鍖栫害鏉熸潯浠跺垪琛� + this.fieldConstraintsList = []; + // 鍒濆鍖栬嚜瀹氫箟灞炴�у垪琛� + this.fieldPropertiesList = []; + } + this.fieldModelVisible = true; + }, + // 鎵撳紑瀛楁 鏌愪釜 閰嶇疆椤� 寮圭獥 + openFieldOptionForm(option, index, type) { + this.fieldOptionModelVisible = true; + this.fieldOptionType = type; + this.formFieldOptionIndex = index; + if (type === 'property') { + this.fieldOptionForm = option ? JSON.parse(JSON.stringify(option)) : {}; + return (this.optionModelTitle = '灞炴�ч厤缃�'); + } + if (type === 'enum') { + this.fieldOptionForm = option ? JSON.parse(JSON.stringify(option)) : {}; + return (this.optionModelTitle = '鏋氫妇鍊奸厤缃�'); + } + this.fieldOptionForm = option ? JSON.parse(JSON.stringify(option)) : {}; + return (this.optionModelTitle = '绾︽潫鏉′欢閰嶇疆'); + }, + + // 淇濆瓨瀛楁 鏌愪釜 閰嶇疆椤� + saveFieldOption() { + if (this.formFieldOptionIndex === -1) { + if (this.fieldOptionType === 'property') { + this.fieldPropertiesList.push(this.fieldOptionForm); + } + if (this.fieldOptionType === 'constraint') { + this.fieldConstraintsList.push(this.fieldOptionForm); + } + if (this.fieldOptionType === 'enum') { + this.fieldEnumList.push(this.fieldOptionForm); + } + } else { + this.fieldOptionType === 'property' && this.fieldPropertiesList.splice(this.formFieldOptionIndex, 1, this.fieldOptionForm); + this.fieldOptionType === 'constraint' && this.fieldConstraintsList.splice(this.formFieldOptionIndex, 1, this.fieldOptionForm); + this.fieldOptionType === 'enum' && this.fieldEnumList.splice(this.formFieldOptionIndex, 1, this.fieldOptionForm); + } + this.fieldOptionModelVisible = false; + this.fieldOptionForm = {}; + }, + // 淇濆瓨瀛楁閰嶇疆 + saveField() { + const { id, type, label, defaultValue, datePattern } = this.formFieldForm; + const Field = window.bpmnInstances.moddle.create(`${this.prefix}:FormField`, { id, type, label }); + defaultValue && (Field.defaultValue = defaultValue); + datePattern && (Field.datePattern = datePattern); + // 鏋勫缓灞炴�� + if (this.fieldPropertiesList && this.fieldPropertiesList.length) { + const fieldPropertyList = this.fieldPropertiesList.map(fp => { + return window.bpmnInstances.moddle.create(`${this.prefix}:Property`, { id: fp.id, value: fp.value }); + }); + Field.properties = window.bpmnInstances.moddle.create(`${this.prefix}:Properties`, { values: fieldPropertyList }); + } + // 鏋勫缓鏍¢獙瑙勫垯 + if (this.fieldConstraintsList && this.fieldConstraintsList.length) { + const fieldConstraintList = this.fieldConstraintsList.map(fc => { + return window.bpmnInstances.moddle.create(`${this.prefix}:Constraint`, { name: fc.name, config: fc.config }); + }); + Field.validation = window.bpmnInstances.moddle.create(`${this.prefix}:Validation`, { constraints: fieldConstraintList }); + } + // 鏋勫缓鏋氫妇鍊� + if (this.fieldEnumList && this.fieldEnumList.length) { + Field.values = this.fieldEnumList.map(fe => { + return window.bpmnInstances.moddle.create(`${this.prefix}:Value`, { name: fe.name, id: fe.id }); + }); + } + // 鏇存柊鏁扮粍 涓� 琛ㄥ崟閰嶇疆瀹炰緥 + if (this.formFieldIndex === -1) { + this.fieldList.push(this.formFieldForm); + this.formData.fields && this.formData.fields.push(Field); + } else { + this.fieldList.splice(this.formFieldIndex, 1, this.formFieldForm); + this.formData.fields.splice(this.formFieldIndex, 1, Field); + } + this.updateElementExtensions(); + this.fieldModelVisible = false; + }, + + // 绉婚櫎鏌愪釜 瀛楁鐨� 閰嶇疆椤� + removeFieldOptionItem(option, index, type) { + if (type === 'property') { + this.fieldPropertiesList.splice(index, 1); + return; + } + if (type === 'enum') { + this.fieldEnumList.splice(index, 1); + return; + } + this.fieldConstraintsList.splice(index, 1); + }, + // 绉婚櫎 瀛楁 + removeField(field, index) { + this.fieldList.splice(index, 1); + this.formData.fields.splice(index, 1); + this.updateElementExtensions(); + }, + + updateElementExtensions() { + // 鏇存柊鍥炴墿灞曞厓绱� + const newElExtensionElements = window.bpmnInstances.moddle.create(`bpmn:ExtensionElements`, { + values: this.otherExtensions.concat(this.formData) + }); + // 鏇存柊鍒板厓绱犱笂 + window.bpmnInstances.modeling.updateProperties(this.bpmnELement, { + extensionElements: newElExtensionElements + }); + } + } +} +</script> diff --git a/ruoyi-ui/apps/web-antd/src/package/penal/index.js b/ruoyi-ui/apps/web-antd/src/package/penal/index.js new file mode 100644 index 0000000..873d555 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/penal/index.js @@ -0,0 +1,7 @@ +import MyPropertiesPanel from './PropertiesPanel.vue'; + +MyPropertiesPanel.install = function(Vue) { + Vue.component(MyPropertiesPanel.name, MyPropertiesPanel); +}; + +export default MyPropertiesPanel; diff --git a/ruoyi-ui/apps/web-antd/src/package/penal/listeners/ElementListeners.vue b/ruoyi-ui/apps/web-antd/src/package/penal/listeners/ElementListeners.vue new file mode 100644 index 0000000..55f7d3c --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/penal/listeners/ElementListeners.vue @@ -0,0 +1,302 @@ +<template> + <div class="panel-tab__content"> + <el-table :data="elementListenersList" size="small" border> + <el-table-column label="搴忓彿" width="50px" type="index" /> + <el-table-column label="浜嬩欢绫诲瀷" min-width="100px" prop="event" /> + <el-table-column label="鐩戝惉鍣ㄧ被鍨�" min-width="100px" show-overflow-tooltip :formatter="row => listenerTypeObject[row.listenerType]" /> + <el-table-column label="鎿嶄綔" width="90px"> + <template v-slot="{ row, $index }"> + <el-button link type="" @click="openListenerForm(row, $index)">缂栬緫</el-button> + <el-divider direction="vertical" /> + <el-button link type="" style="color: #ff4d4f" @click="removeListener(row, $index)">绉婚櫎</el-button> + </template> + </el-table-column> + </el-table> + <div class="element-drawer__button"> + <el-button size="small" type="primary" :icon="Plus" @click="openListenerForm(null)">娣诲姞鐩戝惉鍣�</el-button> + </div> + + <!-- 鐩戝惉鍣� 缂栬緫/鍒涘缓 閮ㄥ垎 --> + <el-drawer v-model="listenerFormModelVisible" title="鎵ц鐩戝惉鍣�" :size="`${width}px`" append-to-body destroy-on-close> + <el-form size="small" :model="listenerForm" label-width="96px" ref="listenerFormRef" @submit.prevent> + <el-form-item label="浜嬩欢绫诲瀷" prop="event" :rules="{ required: true, trigger: ['blur', 'change'] }"> + <el-select v-model="listenerForm.event"> + <el-option label="start" value="start" /> + <el-option label="end" value="end" /> + </el-select> + </el-form-item> + <el-form-item label="鐩戝惉鍣ㄧ被鍨�" prop="listenerType" :rules="{ required: true, trigger: ['blur', 'change'] }"> + <el-select v-model="listenerForm.listenerType"> + <el-option v-for="i in Object.keys(listenerTypeObject)" :key="i" :label="listenerTypeObject[i]" :value="i" /> + </el-select> + </el-form-item> + <el-form-item + v-if="listenerForm.listenerType === 'classListener'" + label="Java绫�" + prop="class" + key="listener-class" + :rules="{ required: true, trigger: ['blur', 'change'] }" + > + <el-input v-model="listenerForm.class" clearable /> + </el-form-item> + <el-form-item + v-if="listenerForm.listenerType === 'expressionListener'" + label="琛ㄨ揪寮�" + prop="expression" + key="listener-expression" + :rules="{ required: true, trigger: ['blur', 'change'] }" + > + <el-input v-model="listenerForm.expression" clearable /> + </el-form-item> + <el-form-item + v-if="listenerForm.listenerType === 'delegateExpressionListener'" + label="浠g悊琛ㄨ揪寮�" + prop="delegateExpression" + key="listener-delegate" + :rules="{ required: true, trigger: ['blur', 'change'] }" + > + <el-input v-model="listenerForm.delegateExpression" clearable /> + </el-form-item> + <template v-if="listenerForm.listenerType === 'scriptListener'"> + <el-form-item + label="鑴氭湰鏍煎紡" + prop="scriptFormat" + key="listener-script-format" + :rules="{ required: true, trigger: ['blur', 'change'], message: '璇峰~鍐欒剼鏈牸寮�' }" + > + <el-input v-model="listenerForm.scriptFormat" clearable /> + </el-form-item> + <el-form-item + label="鑴氭湰绫诲瀷" + prop="scriptType" + key="listener-script-type" + :rules="{ required: true, trigger: ['blur', 'change'], message: '璇烽�夋嫨鑴氭湰绫诲瀷' }" + > + <el-select v-model="listenerForm.scriptType"> + <el-option label="鍐呰仈鑴氭湰" value="inlineScript" /> + <el-option label="澶栭儴鑴氭湰" value="externalScript" /> + </el-select> + </el-form-item> + <el-form-item + v-if="listenerForm.scriptType === 'inlineScript'" + label="鑴氭湰鍐呭" + prop="value" + key="listener-script" + :rules="{ required: true, trigger: ['blur', 'change'], message: '璇峰~鍐欒剼鏈唴瀹�' }" + > + <el-input v-model="listenerForm.value" clearable /> + </el-form-item> + <el-form-item + v-if="listenerForm.scriptType === 'externalScript'" + label="璧勬簮鍦板潃" + prop="resource" + key="listener-resource" + :rules="{ required: true, trigger: ['blur', 'change'], message: '璇峰~鍐欒祫婧愬湴鍧�' }" + > + <el-input v-model="listenerForm.resource" clearable /> + </el-form-item> + </template> + </el-form> + <el-divider /> + <p class="listener-filed__title"> + <span><el-icon><Menu /></el-icon>娉ㄥ叆瀛楁锛�</span> + <el-button size="small" type="primary" @click="openListenerFieldForm(null)">娣诲姞瀛楁</el-button> + </p> + <el-table :data="fieldsListOfListener" size="small" max-height="240" border fit style="flex: none"> + <el-table-column label="搴忓彿" width="50px" type="index" /> + <el-table-column label="瀛楁鍚嶇О" min-width="100px" prop="name" /> + <el-table-column label="瀛楁绫诲瀷" min-width="80px" show-overflow-tooltip :formatter="row => fieldTypeObject[row.fieldType]" /> + <el-table-column label="瀛楁鍊�/琛ㄨ揪寮�" min-width="100px" show-overflow-tooltip :formatter="row => row.string || row.expression" /> + <el-table-column label="鎿嶄綔" width="100px"> + <template v-slot="{ row, $index }"> + <el-button link type="" @click="openListenerFieldForm(row, $index)">缂栬緫</el-button> + <el-divider direction="vertical" /> + <el-button link type="" style="color: #ff4d4f" @click="removeListenerField(row, $index)">绉婚櫎</el-button> + </template> + </el-table-column> + </el-table> + + <div class="element-drawer__button"> + <el-button size="small" @click="listenerFormModelVisible = false">鍙� 娑�</el-button> + <el-button size="small" type="primary" @click="saveListenerConfig">淇� 瀛�</el-button> + </div> + </el-drawer> + + <!-- 娉ㄥ叆瑗挎 缂栬緫/鍒涘缓 閮ㄥ垎 --> + <el-dialog title="瀛楁閰嶇疆" v-model="listenerFieldFormModelVisible" width="600px" append-to-body destroy-on-close> + <el-form :model="listenerFieldForm" size="small" label-width="96px" ref="listenerFieldFormRef" style="height: 136px" @submit.prevent> + <el-form-item label="瀛楁鍚嶇О锛�" prop="name" :rules="{ required: true, trigger: ['blur', 'change'] }"> + <el-input v-model="listenerFieldForm.name" clearable /> + </el-form-item> + <el-form-item label="瀛楁绫诲瀷锛�" prop="fieldType" :rules="{ required: true, trigger: ['blur', 'change'] }"> + <el-select v-model="listenerFieldForm.fieldType"> + <el-option v-for="i in Object.keys(fieldTypeObject)" :key="i" :label="fieldTypeObject[i]" :value="i" /> + </el-select> + </el-form-item> + <el-form-item + v-if="listenerFieldForm.fieldType === 'string'" + label="瀛楁鍊硷細" + prop="string" + key="field-string" + :rules="{ required: true, trigger: ['blur', 'change'] }" + > + <el-input v-model="listenerFieldForm.string" clearable /> + </el-form-item> + <el-form-item + v-if="listenerFieldForm.fieldType === 'expression'" + label="琛ㄨ揪寮忥細" + prop="expression" + key="field-expression" + :rules="{ required: true, trigger: ['blur', 'change'] }" + > + <el-input v-model="listenerFieldForm.expression" clearable /> + </el-form-item> + </el-form> + <template v-slot:footer> + <el-button size="small" @click="listenerFieldFormModelVisible = false">鍙� 娑�</el-button> + <el-button size="small" type="primary" @click="saveListenerFiled">纭� 瀹�</el-button> + </template> + </el-dialog> + </div> +</template> +<script> +import { createListenerObject, updateElementExtensions } from "../../utils"; +import { initListenerType, initListenerForm, LISTENER_TYPE, FIELD_TYPE } from "./utilSelf"; + +export default { + name: "ElementListeners", + setup() { + return { Plus } + }, + props: { + id: String, + type: String + }, + inject: { + prefix: "prefix", + width: "width" + }, + data() { + return { + elementListenersList: [], // 鐩戝惉鍣ㄥ垪琛� + listenerForm: {}, // 鐩戝惉鍣ㄨ鎯呰〃鍗� + listenerFormModelVisible: false, // 鐩戝惉鍣� 缂栬緫 渚ц竟鏍忔樉绀虹姸鎬� + fieldsListOfListener: [], + listenerFieldForm: {}, // 鐩戝惉鍣� 娉ㄥ叆瀛楁 璇︽儏琛ㄥ崟 + listenerFieldFormModelVisible: false, // 鐩戝惉鍣� 娉ㄥ叆瀛楁琛ㄥ崟寮圭獥 鏄剧ず鐘舵�� + editingListenerIndex: -1, // 鐩戝惉鍣ㄦ墍鍦ㄤ笅鏍囷紝-1 涓烘柊澧� + editingListenerFieldIndex: -1, // 瀛楁鎵�鍦ㄤ笅鏍囷紝-1 涓烘柊澧� + listenerTypeObject: LISTENER_TYPE, + fieldTypeObject: FIELD_TYPE + }; + }, + watch: { + id: { + immediate: true, + handler(val) { + val && val.length && this.$nextTick(() => this.resetListenersList()); + } + } + }, + methods: { + resetListenersList() { + this.bpmnElement = window.bpmnInstances.bpmnElement; + this.otherExtensionList = []; + this.bpmnElementListeners = + this.bpmnElement.businessObject?.extensionElements?.values?.filter(ex => ex.$type === `${this.prefix}:ExecutionListener`) ?? []; + this.elementListenersList = this.bpmnElementListeners.map(listener => initListenerType(listener)); + }, + // 鎵撳紑 鐩戝惉鍣ㄨ鎯� 渚ц竟鏍� + openListenerForm(listener, index) { + if (listener) { + this.listenerForm = initListenerForm(listener); + this.editingListenerIndex = index; + } else { + this.listenerForm = {}; + this.editingListenerIndex = -1; // 鏍囪涓烘柊澧� + } + if (listener && listener.fields) { + this.fieldsListOfListener = listener.fields.map(field => ({ + ...field, + fieldType: field.string ? "string" : "expression" + })); + } else { + this.fieldsListOfListener = []; + this.listenerForm["fields"] = [] + } + // 鎵撳紑渚ц竟鏍忓苟娓呮楠岃瘉鐘舵�� + this.listenerFormModelVisible = true; + this.$nextTick(() => { + if (this.$refs["listenerFormRef"]) this.$refs["listenerFormRef"].clearValidate(); + }); + }, + // 鎵撳紑鐩戝惉鍣ㄥ瓧娈电紪杈戝脊绐� + openListenerFieldForm(field, index) { + this.listenerFieldForm = field ? JSON.parse(JSON.stringify(field)) : {}; + this.editingListenerFieldIndex = field ? index : -1; + this.listenerFieldFormModelVisible = true; + this.$nextTick(() => { + if (this.$refs["listenerFieldFormRef"]) this.$refs["listenerFieldFormRef"].clearValidate(); + }); + }, + // 淇濆瓨鐩戝惉鍣ㄦ敞鍏ュ瓧娈� + async saveListenerFiled() { + let validateStatus = await this.$refs["listenerFieldFormRef"].validate(); + if (!validateStatus) return; // 楠岃瘉涓嶉�氳繃鐩存帴杩斿洖 + if (this.editingListenerFieldIndex === -1) { + this.fieldsListOfListener.push(this.listenerFieldForm); + this.listenerForm.fields.push(this.listenerFieldForm); + } else { + this.fieldsListOfListener.splice(this.editingListenerFieldIndex, 1, this.listenerFieldForm); + this.listenerForm.fields.splice(this.editingListenerFieldIndex, 1, this.listenerFieldForm); + } + this.listenerFieldFormModelVisible = false; + this.$nextTick(() => (this.listenerFieldForm = {})); + }, + // 绉婚櫎鐩戝惉鍣ㄥ瓧娈� + removeListenerField(field, index) { + this.$confirm("纭绉婚櫎璇ュ瓧娈靛悧锛�", "鎻愮ず", { + confirmButtonText: "纭� 璁�", + cancelButtonText: "鍙� 娑�" + }) + .then(() => { + this.fieldsListOfListener.splice(index, 1); + this.listenerForm.fields.splice(index, 1); + }) + .catch(() => console.info("鎿嶄綔鍙栨秷")); + }, + // 绉婚櫎鐩戝惉鍣� + removeListener(listener, index) { + this.$confirm("纭绉婚櫎璇ョ洃鍚櫒鍚楋紵", "鎻愮ず", { + confirmButtonText: "纭� 璁�", + cancelButtonText: "鍙� 娑�" + }) + .then(() => { + this.bpmnElementListeners.splice(index, 1); + this.elementListenersList.splice(index, 1); + updateElementExtensions(this.bpmnElement, this.otherExtensionList.concat(this.bpmnElementListeners)); + }) + .catch(() => console.info("鎿嶄綔鍙栨秷")); + }, + // 淇濆瓨鐩戝惉鍣ㄩ厤缃� + async saveListenerConfig() { + let validateStatus = await this.$refs["listenerFormRef"].validate(); + if (!validateStatus) return; // 楠岃瘉涓嶉�氳繃鐩存帴杩斿洖 + const listenerObject = createListenerObject(this.listenerForm, false, this.prefix); + if (this.editingListenerIndex === -1) { + this.bpmnElementListeners.push(listenerObject); + this.elementListenersList.push(this.listenerForm); + } else { + this.bpmnElementListeners.splice(this.editingListenerIndex, 1, listenerObject); + this.elementListenersList.splice(this.editingListenerIndex, 1, this.listenerForm); + } + // 淇濆瓨鍏朵粬閰嶇疆 + this.otherExtensionList = this.bpmnElement.businessObject?.extensionElements?.values?.filter(ex => ex.$type !== `${this.prefix}:ExecutionListener`) ?? []; + updateElementExtensions(this.bpmnElement, this.otherExtensionList.concat(this.bpmnElementListeners)); + // 4. 闅愯棌渚ц竟鏍� + this.listenerFormModelVisible = false; + this.listenerForm = {}; + } + } +}; +</script> diff --git a/ruoyi-ui/apps/web-antd/src/package/penal/listeners/UserTaskListeners.vue b/ruoyi-ui/apps/web-antd/src/package/penal/listeners/UserTaskListeners.vue new file mode 100644 index 0000000..3644da8 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/penal/listeners/UserTaskListeners.vue @@ -0,0 +1,335 @@ +<template> + <div class="panel-tab__content"> + <el-table :data="elementListenersList" size="small" border> + <el-table-column label="搴忓彿" width="50px" type="index" /> + <el-table-column label="浜嬩欢绫诲瀷" min-width="80px" show-overflow-tooltip :formatter="formatterEvent" /> + <!-- <el-table-column label="浜嬩欢id" min-width="80px" prop="id" show-overflow-tooltip />--> + <el-table-column label="鐩戝惉鍣ㄧ被鍨�" min-width="80px" show-overflow-tooltip :formatter="formatterListener" /> + <el-table-column label="鎿嶄綔" width="90px"> + <template v-slot="{ row, $index }"> + <el-button link type="primary" @click="openListenerForm(row, $index)">缂栬緫</el-button> + <el-button link type="danger" @click="removeListener(row, $index)">绉婚櫎</el-button> + </template> + </el-table-column> + </el-table> + <div class="element-drawer__button"> + <el-button size="small" type="primary" :icon="Plus" @click="openListenerForm(null)">娣诲姞鐩戝惉鍣�</el-button> + </div> + + <!-- 鐩戝惉鍣� 缂栬緫/鍒涘缓 閮ㄥ垎 --> + <el-drawer + v-model="listenerFormModelVisible" + title="浠诲姟鐩戝惉鍣�" + :size="`${width}px`" + class="listener-drawer" + append-to-body + destroy-on-close> + <el-form :model="listenerForm" label-width="96px" ref="listenerFormRef" @submit.prevent> + <el-form-item label="浜嬩欢绫诲瀷" prop="event" :rules="{ required: true, trigger: ['blur', 'change'], message: '璇烽�夋嫨浜嬩欢绫诲瀷' }"> + <el-select v-model="listenerForm.event" style="width: 100%"> + <el-option v-for="item in TASK_EVENT_TYPE" :key="item.value" :label="item.label" :value="item.value" /> + </el-select> + </el-form-item> + <el-form-item label="鐩戝惉鍣ㄧ被鍨�" prop="listenerType" :rules="{ required: true, trigger: ['blur', 'change'], message: '璇烽�夋嫨鐩戝惉鍣ㄧ被鍨�' }"> + <el-input + v-model="listenerForm[selectedProp]" + clearable + class="input-with-select"> + <template #prepend> + <el-select v-model="listenerForm.listenerType" style="width: 100%"> + <el-option v-for="item in LISTENER_TYPE" :key="item.key" :label="item.label" :value="item.value" /> + </el-select> + </template> + <template #append> + <el-button :icon="Search" /> + </template> + </el-input> + + </el-form-item> + <!-- 鑴氭湰绫诲瀷 --> + <!-- <template v-if="listenerForm.listenerType === LISTENER_TYPE[LISTENER_TYPE.length - 1].value">--> + <!-- <el-form-item--> + <!-- label="鑴氭湰鏍煎紡"--> + <!-- prop="scriptFormat"--> + <!-- key="listener-script-format"--> + <!-- :rules="{ required: true, trigger: ['blur', 'change'], message: '璇峰~鍐欒剼鏈牸寮�' }"--> + <!-- >--> + <!-- <el-input v-model="listenerForm.scriptFormat" clearable />--> + <!-- </el-form-item>--> + <!-- <el-form-item--> + <!-- label="鑴氭湰绫诲瀷"--> + <!-- prop="scriptType"--> + <!-- key="listener-script-type"--> + <!-- :rules="{ required: true, trigger: ['blur', 'change'], message: '璇烽�夋嫨鑴氭湰绫诲瀷' }"--> + <!-- >--> + <!-- <el-select v-model="listenerForm.scriptType">--> + <!-- <el-option v-for="item in SCRIPT_TYPE" :key="item.value" :label="item.label" :value="item.value" />--> + <!-- </el-select>--> + <!-- </el-form-item>--> + <!-- <el-form-item--> + <!-- v-if="listenerForm.scriptType === SCRIPT_TYPE[0].value"--> + <!-- label="鑴氭湰鍐呭"--> + <!-- prop="value"--> + <!-- key="listener-script"--> + <!-- :rules="{ required: true, trigger: ['blur', 'change'], message: '璇峰~鍐欒剼鏈唴瀹�' }"--> + <!-- >--> + <!-- <el-input v-model="listenerForm.value" clearable />--> + <!-- </el-form-item>--> + <!-- <el-form-item--> + <!-- v-if="listenerForm.scriptType === SCRIPT_TYPE[1].value"--> + <!-- label="璧勬簮鍦板潃"--> + <!-- prop="resource"--> + <!-- key="listener-resource"--> + <!-- :rules="{ required: true, trigger: ['blur', 'change'], message: '璇峰~鍐欒祫婧愬湴鍧�' }"--> + <!-- >--> + <!-- <el-input v-model="listenerForm.resource" clearable />--> + <!-- </el-form-item>--> + <!-- </template>--> + <!-- 鐩戝惉浜嬩欢锛� 瓒呮椂 --> + <template v-if="listenerForm.event === TASK_EVENT_TYPE[TASK_EVENT_TYPE.length - 1].value"> + <el-form-item label="瀹氭椂鍣ㄧ被鍨�" prop="eventDefinitionType" key="eventDefinitionType"> + <el-select v-model="listenerForm.eventDefinitionType"> + <el-option v-for="item in EVENT_DEFINITION_TYPE" :key="item.value" :value="item.value" :label="item.label" /> + </el-select> + </el-form-item> + <el-form-item + v-if="!!listenerForm.eventDefinitionType && listenerForm.eventDefinitionType !== 'null'" + label="瀹氭椂鍣�" + prop="eventTimeDefinitions" + key="eventTimeDefinitions" + :rules="{ required: true, trigger: ['blur', 'change'], message: '璇峰~鍐欏畾鏃跺櫒閰嶇疆' }" + > + <el-input v-model="listenerForm.eventTimeDefinitions" clearable /> + </el-form-item> + </template> + </el-form> + <el-divider /> + <div class="listener-filed__title"> + <span><el-icon><Menu /></el-icon>娉ㄥ叆瀛楁锛�</span> + <el-button size="small" type="primary" @click="openListenerFieldForm(null)">娣诲姞瀛楁</el-button> + </div> + <el-table :data="fieldsListOfListener" size="small" max-height="240" border fit style="flex: none"> + <el-table-column label="搴忓彿" width="50px" type="index" /> + <el-table-column label="瀛楁鍚嶇О" min-width="100px" prop="name" /> + <el-table-column label="瀛楁绫诲瀷" min-width="80px" show-overflow-tooltip :formatter="formatterField" /> + <el-table-column label="瀛楁鍊�/琛ㄨ揪寮�" min-width="100px" show-overflow-tooltip :formatter="row => row.string || row.expression" /> + <el-table-column label="鎿嶄綔" width="100px"> + <template v-slot="{ row, $index }"> + <el-button link type="primary" @click="openListenerFieldForm(row, $index)">缂栬緫</el-button> + <el-button link type="danger" @click="removeListenerField(row, $index)">绉婚櫎</el-button> + </template> + </el-table-column> + </el-table> + + <div class="element-drawer__button"> + <el-button @click="listenerFormModelVisible = false">鍙� 娑�</el-button> + <el-button type="primary" @click="saveListenerConfig">淇� 瀛�</el-button> + </div> + </el-drawer> + + <!-- 娉ㄥ叆瑗挎 缂栬緫/鍒涘缓 閮ㄥ垎 --> + <el-dialog title="瀛楁閰嶇疆" v-model="listenerFieldFormModelVisible" width="600px" append-to-body destroy-on-close> + <el-form :model="listenerFieldForm" size="small" label-width="96px" ref="listenerFieldFormRef" style="height: 136px" @submit.prevent> + <el-form-item label="瀛楁鍚嶇О锛�" prop="name" :rules="{ required: true, trigger: ['blur', 'change'], message: '璇峰~鍐欏瓧娈靛悕绉�' }"> + <el-input v-model="listenerFieldForm.name" clearable /> + </el-form-item> + <el-form-item label="瀛楁绫诲瀷锛�" prop="fieldType" :rules="{ required: true, trigger: ['blur', 'change'], message: '璇烽�夋嫨瀛楁绫诲瀷' }"> + <el-select v-model="listenerFieldForm.fieldType"> + <el-option v-for="item in FIELD_TYPE" :key="item.value" :label="item.label" :value="item.value" /> + </el-select> + </el-form-item> + <el-form-item + v-if="listenerFieldForm.fieldType === FIELD_TYPE[0].value" + label="瀛楁鍊硷細" + prop="string" + key="field-string" + :rules="{ required: true, trigger: ['blur', 'change'], message: '璇峰~鍐欏瓧娈靛��' }" + > + <el-input v-model="listenerFieldForm.string" clearable /> + </el-form-item> + <el-form-item + v-if="listenerFieldForm.fieldType === FIELD_TYPE[1].value" + label="琛ㄨ揪寮忥細" + prop="expression" + key="field-expression" + :rules="{ required: true, trigger: ['blur', 'change'], message: '璇峰~鍐欒〃杈惧紡' }" + > + <el-input v-model="listenerFieldForm.expression" clearable /> + </el-form-item> + </el-form> + <template v-slot:footer> + <el-button @click="listenerFieldFormModelVisible = false">鍙� 娑�</el-button> + <el-button type="primary" @click="saveListenerFiled">纭� 瀹�</el-button> + </template> + </el-dialog> + </div> +</template> +<script> +import { createListenerObject, updateElementExtensions } from "../../utils"; +import { + initListenerForm, + initListenerType, + FIELD_TYPE, + LISTENER_TYPE, + TASK_EVENT_TYPE, + SCRIPT_TYPE, + EVENT_DEFINITION_TYPE +} from "./utilSelf"; +export default { + name: "UserTaskListeners", + props: { + id: String, + type: String + }, + inject: { + prefix: "prefix", + width: "width" + }, + setup() { + return { Plus, Search } + }, + data() { + return { + elementListenersList: [], + listenerFormModelVisible: false, + listenerForm: {}, + fieldsListOfListener: [], + listenerFieldFormModelVisible: false, // 鐩戝惉鍣� 娉ㄥ叆瀛楁琛ㄥ崟寮圭獥 鏄剧ず鐘舵�� + editingListenerIndex: -1, // 鐩戝惉鍣ㄦ墍鍦ㄤ笅鏍囷紝-1 涓烘柊澧� + editingListenerFieldIndex: -1, // 瀛楁鎵�鍦ㄤ笅鏍囷紝-1 涓烘柊澧� + listenerFieldForm: {} // 鐩戝惉鍣� 娉ㄥ叆瀛楁 璇︽儏琛ㄥ崟 + }; + }, + watch: { + id: { + immediate: true, + handler(val) { + val && val.length && this.$nextTick(() => this.resetListenersList()); + } + } + }, + computed: { + TASK_EVENT_TYPE() { return TASK_EVENT_TYPE }, + LISTENER_TYPE() { return LISTENER_TYPE }, + FIELD_TYPE() { return FIELD_TYPE }, + SCRIPT_TYPE() { return SCRIPT_TYPE }, + EVENT_DEFINITION_TYPE() { return EVENT_DEFINITION_TYPE }, + formatterEvent() { + return (row) => { + return TASK_EVENT_TYPE.find(find => find.value === row.event)?.label || "" + } + }, + formatterListener() { + return (row) => { + return LISTENER_TYPE.find(find => find.value === row.listener)?.label || "" + } + }, + formatterField() { + return (row) => { + return FIELD_TYPE.find(find => find.value === row.fieldType)?.label || "" + } + }, + selectedProp() { + return LISTENER_TYPE.find(find => find.value === this.listenerForm.listenerType)?.prop || '' + } + }, + methods: { + resetListenersList() { + this.bpmnElement = window.bpmnInstances.bpmnElement; + this.otherExtensionList = []; + this.bpmnElementListeners = this.bpmnElement.businessObject?.extensionElements?.values?.filter(ex => ex.$type === `${this.prefix}:TaskListener`) ?? []; + this.elementListenersList = this.bpmnElementListeners.map(listener => initListenerType(listener)); + }, + openListenerForm(listener, index) { + if (listener) { + this.listenerForm = initListenerForm(listener); + this.editingListenerIndex = index; + } else { + this.listenerForm = {}; + this.editingListenerIndex = -1; // 鏍囪涓烘柊澧� + } + if (listener && listener.fields) { + this.fieldsListOfListener = listener.fields.map(field => ({ + ...field, + fieldType: field.string ? "string" : "expression" + })); + } else { + this.fieldsListOfListener = []; + this.listenerForm["fields"] = [] + } + // 鎵撳紑渚ц竟鏍忓苟娓呮楠岃瘉鐘舵�� + this.listenerFormModelVisible = true; + this.$nextTick(() => { + if (this.$refs["listenerFormRef"]) this.$refs["listenerFormRef"].clearValidate(); + }); + }, + // 绉婚櫎鐩戝惉鍣� + removeListener(listener, index) { + this.$confirm("纭绉婚櫎璇ョ洃鍚櫒鍚楋紵", "鎻愮ず", { + confirmButtonText: "纭� 璁�", + cancelButtonText: "鍙� 娑�" + }) + .then(() => { + this.bpmnElementListeners.splice(index, 1); + this.elementListenersList.splice(index, 1); + updateElementExtensions(this.bpmnElement, this.otherExtensionList.concat(this.bpmnElementListeners)); + }) + .catch(() => console.info("鎿嶄綔鍙栨秷")); + }, + // 淇濆瓨鐩戝惉鍣� + async saveListenerConfig() { + let validateStatus = await this.$refs["listenerFormRef"].validate(); + if (!validateStatus) return; // 楠岃瘉涓嶉�氳繃鐩存帴杩斿洖 + const listenerObject = createListenerObject(this.listenerForm, true, this.prefix); + if (this.editingListenerIndex === -1) { + this.bpmnElementListeners.push(listenerObject); + this.elementListenersList.push(this.listenerForm); + } else { + this.bpmnElementListeners.splice(this.editingListenerIndex, 1, listenerObject); + this.elementListenersList.splice(this.editingListenerIndex, 1, this.listenerForm); + } + // 淇濆瓨鍏朵粬閰嶇疆 + this.otherExtensionList = this.bpmnElement.businessObject?.extensionElements?.values?.filter(ex => ex.$type !== `${this.prefix}:TaskListener`) ?? []; + updateElementExtensions(this.bpmnElement, this.otherExtensionList.concat(this.bpmnElementListeners)); + // 4. 闅愯棌渚ц竟鏍� + this.listenerFormModelVisible = false; + this.listenerForm = {}; + }, + // 鎵撳紑鐩戝惉鍣ㄥ瓧娈电紪杈戝脊绐� + openListenerFieldForm(field, index) { + this.listenerFieldForm = field ? JSON.parse(JSON.stringify(field)) : {}; + this.editingListenerFieldIndex = field ? index : -1; + this.listenerFieldFormModelVisible = true; + this.$nextTick(() => { + if (this.$refs["listenerFieldFormRef"]) this.$refs["listenerFieldFormRef"].clearValidate(); + }); + }, + // 淇濆瓨鐩戝惉鍣ㄦ敞鍏ュ瓧娈� + async saveListenerFiled() { + let validateStatus = await this.$refs["listenerFieldFormRef"].validate(); + if (!validateStatus) return; // 楠岃瘉涓嶉�氳繃鐩存帴杩斿洖 + if (this.editingListenerFieldIndex === -1) { + this.fieldsListOfListener.push(this.listenerFieldForm); + this.listenerForm.fields.push(this.listenerFieldForm); + } else { + this.fieldsListOfListener.splice(this.editingListenerFieldIndex, 1, this.listenerFieldForm); + this.listenerForm.fields.splice(this.editingListenerFieldIndex, 1, this.listenerFieldForm); + } + this.listenerFieldFormModelVisible = false; + this.$nextTick(() => (this.listenerFieldForm = {})); + }, + // 绉婚櫎鐩戝惉鍣ㄥ瓧娈� + removeListenerField(field, index) { + this.$confirm("纭绉婚櫎璇ュ瓧娈靛悧锛�", "鎻愮ず", { + confirmButtonText: "纭� 璁�", + cancelButtonText: "鍙� 娑�" + }) + .then(() => { + this.fieldsListOfListener.splice(index, 1); + this.listenerForm.fields.splice(index, 1); + }) + .catch(() => console.info("鎿嶄綔鍙栨秷")); + } + } +}; +</script> diff --git a/ruoyi-ui/apps/web-antd/src/package/penal/listeners/template.js b/ruoyi-ui/apps/web-antd/src/package/penal/listeners/template.js new file mode 100644 index 0000000..2839c1f --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/penal/listeners/template.js @@ -0,0 +1,178 @@ +export const template = isTaskListener => { + return ` + <div class="panel-tab__content"> + <el-table :data="elementListenersList" size="small" border> + <el-table-column label="搴忓彿" width="50px" type="index" /> + <el-table-column label="浜嬩欢绫诲瀷" min-width="100px" prop="event" /> + <el-table-column label="鐩戝惉鍣ㄧ被鍨�" min-width="100px" show-overflow-tooltip :formatter="row => listenerTypeObject[row.listenerType]" /> + <el-table-column label="鎿嶄綔" width="90px"> + <template v-slot="{ row, $index }"> + <el-button link type="" @click="openListenerForm(row, $index)">缂栬緫</el-button> + <el-divider direction="vertical" /> + <el-button link type="" style="color: #ff4d4f" @click="removeListener(row, $index)">绉婚櫎</el-button> + </template> + </el-table-column> + </el-table> + <div class="element-drawer__button"> + <el-button size="small" type="primary" :icon="Plus" @click="openListenerForm(null)">娣诲姞鐩戝惉鍣�</el-button> + </div> + + <!-- 鐩戝惉鍣� 缂栬緫/鍒涘缓 閮ㄥ垎 --> + <el-drawer v-model="listenerFormModelVisible" title="鎵ц鐩戝惉鍣�" :size="width + 'px'" append-to-body destroy-on-close> + <el-form size="small" :model="listenerForm" label-width="96px" ref="listenerFormRef" @submit.prevent> + <el-form-item label="浜嬩欢绫诲瀷" prop="event" :rules="{ required: true, trigger: ['blur', 'change'] }"> + <el-select v-model="listenerForm.event"> + <el-option label="start" value="start" /> + <el-option label="end" value="end" /> + </el-select> + </el-form-item> + <el-form-item label="鐩戝惉鍣ㄧ被鍨�" prop="listenerType" :rules="{ required: true, trigger: ['blur', 'change'] }"> + <el-select v-model="listenerForm.listenerType"> + <el-option v-for="i in Object.keys(listenerTypeObject)" :key="i" :label="listenerTypeObject[i]" :value="i" /> + </el-select> + </el-form-item> + <el-form-item + v-if="listenerForm.listenerType === 'classListener'" + label="Java绫�" + prop="class" + key="listener-class" + :rules="{ required: true, trigger: ['blur', 'change'] }" + > + <el-input v-model="listenerForm.class" clearable /> + </el-form-item> + <el-form-item + v-if="listenerForm.listenerType === 'expressionListener'" + label="琛ㄨ揪寮�" + prop="expression" + key="listener-expression" + :rules="{ required: true, trigger: ['blur', 'change'] }" + > + <el-input v-model="listenerForm.expression" clearable /> + </el-form-item> + <el-form-item + v-if="listenerForm.listenerType === 'delegateExpressionListener'" + label="浠g悊琛ㄨ揪寮�" + prop="delegateExpression" + key="listener-delegate" + :rules="{ required: true, trigger: ['blur', 'change'] }" + > + <el-input v-model="listenerForm.delegateExpression" clearable /> + </el-form-item> + <template v-if="listenerForm.listenerType === 'scriptListener'"> + <el-form-item + label="鑴氭湰鏍煎紡" + prop="scriptFormat" + key="listener-script-format" + :rules="{ required: true, trigger: ['blur', 'change'], message: '璇峰~鍐欒剼鏈牸寮�' }" + > + <el-input v-model="listenerForm.scriptFormat" clearable /> + </el-form-item> + <el-form-item + label="鑴氭湰绫诲瀷" + prop="scriptType" + key="listener-script-type" + :rules="{ required: true, trigger: ['blur', 'change'], message: '璇烽�夋嫨鑴氭湰绫诲瀷' }" + > + <el-select v-model="listenerForm.scriptType"> + <el-option label="鍐呰仈鑴氭湰" value="inlineScript" /> + <el-option label="澶栭儴鑴氭湰" value="externalScript" /> + </el-select> + </el-form-item> + <el-form-item + v-if="listenerForm.scriptType === 'inlineScript'" + label="鑴氭湰鍐呭" + prop="value" + key="listener-script" + :rules="{ required: true, trigger: ['blur', 'change'], message: '璇峰~鍐欒剼鏈唴瀹�' }" + > + <el-input v-model="listenerForm.value" clearable /> + </el-form-item> + <el-form-item + v-if="listenerForm.scriptType === 'externalScript'" + label="璧勬簮鍦板潃" + prop="resource" + key="listener-resource" + :rules="{ required: true, trigger: ['blur', 'change'], message: '璇峰~鍐欒祫婧愬湴鍧�' }" + > + <el-input v-model="listenerForm.resource" clearable /> + </el-form-item> + </template> + ${ + isTaskListener + ? "<el-form-item label='瀹氭椂鍣ㄧ被鍨�' prop='eventDefinitionType' key='eventDefinitionType'>" + + "<el-select v-model='listenerForm.eventDefinitionType'>" + + "<el-option label='鏃ユ湡' value='date' />" + + "<el-option label='鎸佺画鏃堕暱' value='duration' />" + + "<el-option label='寰幆' value='cycle' />" + + "<el-option label='鏃�' value='' />" + + '</el-select>' + + '</el-form-item>' + + "<el-form-item v-if='!!listenerForm.eventDefinitionType' label='瀹氭椂鍣�' prop='eventDefinitions' key='eventDefinitions'>" + + "<el-input v-model='listenerForm.eventDefinitions' clearable />" + + '</el-form-item>' + : '' +} + </el-form> + <el-divider /> + <p class="listener-filed__title"> + <span><el-icon><Menu /></el-icon>娉ㄥ叆瀛楁锛�</span> + <el-button size="small" type="primary" @click="openListenerFieldForm(null)">娣诲姞瀛楁</el-button> + </p> + <el-table :data="fieldsListOfListener" size="small" max-height="240" border fit style="flex: none"> + <el-table-column label="搴忓彿" width="50px" type="index" /> + <el-table-column label="瀛楁鍚嶇О" min-width="100px" prop="name" /> + <el-table-column label="瀛楁绫诲瀷" min-width="80px" show-overflow-tooltip :formatter="row => fieldTypeObject[row.fieldType]" /> + <el-table-column label="瀛楁鍊�/琛ㄨ揪寮�" min-width="100px" show-overflow-tooltip :formatter="row => row.string || row.expression" /> + <el-table-column label="鎿嶄綔" width="100px"> + <template v-slot="{ row, $index }"> + <el-button link type="" @click="openListenerFieldForm(row, $index)">缂栬緫</el-button> + <el-divider direction="vertical" /> + <el-button link type="" style="color: #ff4d4f" @click="removeListenerField(row, $index)">绉婚櫎</el-button> + </template> + </el-table-column> + </el-table> + + <div class="element-drawer__button"> + <el-button size="small" @click="listenerFormModelVisible = false">鍙� 娑�</el-button> + <el-button size="small" type="primary" @click="saveListenerConfig">淇� 瀛�</el-button> + </div> + </el-drawer> + + <!-- 娉ㄥ叆瑗挎 缂栬緫/鍒涘缓 閮ㄥ垎 --> + <el-dialog title="瀛楁閰嶇疆" v-model="listenerFieldFormModelVisible" width="600px" append-to-body destroy-on-close> + <el-form :model="listenerFieldForm" size="small" label-width="96px" ref="listenerFieldFormRef" style="height: 136px" @submit.prevent> + <el-form-item label="瀛楁鍚嶇О锛�" prop="name" :rules="{ required: true, trigger: ['blur', 'change'] }"> + <el-input v-model="listenerFieldForm.name" clearable /> + </el-form-item> + <el-form-item label="瀛楁绫诲瀷锛�" prop="fieldType" :rules="{ required: true, trigger: ['blur', 'change'] }"> + <el-select v-model="listenerFieldForm.fieldType"> + <el-option v-for="i in Object.keys(fieldTypeObject)" :key="i" :label="fieldTypeObject[i]" :value="i" /> + </el-select> + </el-form-item> + <el-form-item + v-if="listenerFieldForm.fieldType === 'string'" + label="瀛楁鍊硷細" + prop="string" + key="field-string" + :rules="{ required: true, trigger: ['blur', 'change'] }" + > + <el-input v-model="listenerFieldForm.string" clearable /> + </el-form-item> + <el-form-item + v-if="listenerFieldForm.fieldType === 'expression'" + label="琛ㄨ揪寮忥細" + prop="expression" + key="field-expression" + :rules="{ required: true, trigger: ['blur', 'change'] }" + > + <el-input v-model="listenerFieldForm.expression" clearable /> + </el-form-item> + </el-form> + <template v-slot:footer> + <el-button size="small" @click="listenerFieldFormModelVisible = false">鍙� 娑�</el-button> + <el-button size="small" type="primary" @click="saveListenerFiled">纭� 瀹�</el-button> + </template> + </el-dialog> + </div> + `; +}; diff --git a/ruoyi-ui/apps/web-antd/src/package/penal/listeners/utilSelf.js b/ruoyi-ui/apps/web-antd/src/package/penal/listeners/utilSelf.js new file mode 100644 index 0000000..a90de97 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/penal/listeners/utilSelf.js @@ -0,0 +1,81 @@ +// 鍒濆鍖栬〃鍗曟暟鎹� +export function initListenerForm(listener) { + let self = { + ...listener + }; + if (listener.script) { + self = { + ...listener, + ...listener.script, + scriptType: listener.script.resource ? "externalScript" : "inlineScript" + }; + } + if (listener.event === "timeout" && listener.eventDefinitions) { + if (listener.eventDefinitions.length) { + let k = ""; + for (let key in listener.eventDefinitions[0]) { + console.log(listener.eventDefinitions, key); + if (key.indexOf("time") !== -1) { + k = key; + self.eventDefinitionType = key.replace("time", "").toLowerCase(); + } + } + console.log(k); + self.eventTimeDefinitions = listener.eventDefinitions[0][k].body; + } + } + return self; +} + +export function initListenerType(listener) { + let listenerType; + if (listener.class) listenerType = "classListener"; + if (listener.expression) listenerType = "expressionListener"; + if (listener.delegateExpression) listenerType = "delegateExpressionListener"; + if (listener.script) listenerType = "scriptListener"; + return { + ...JSON.parse(JSON.stringify(listener)), + ...(listener.script ?? {}), + listenerType: listenerType + }; +} + +// 鐩戝惉绫诲瀷 +export const LISTENER_TYPE = [ + { label: "Java 绫�", value: "classListener", prop: "class", key: "listener-class" }, + { label: "琛ㄨ揪寮�", value: "expressionListener", prop: "expression", key: "listener-expression" }, + { label: "浠g悊琛ㄨ揪寮�", value: "delegateExpressionListener", prop: "delegateExpression", key: "listener-delegate" }, + // { label: "鑴氭湰", value: "scriptListener", prop: "scriptFormat", key: "listener-script-format" }, +] +// 鑴氭湰绫诲瀷 +export const SCRIPT_TYPE = [ + { label: "鍐呰仈鑴氭湰", value: "inlineScript" }, + { label: "澶栭儴鑴氭湰", value: "externalScript" }, +] +// 浠诲姟鐩戝惉鍣�: 浜嬩欢绫诲瀷 +export const TASK_EVENT_TYPE = [ + { label: "鍒涘缓", value: "create" }, + { label: "鎸囨淳", value: "assignment" }, + { label: "瀹屾垚", value: "complete" }, + { label: "鍒犻櫎", value: "delete" }, + { label: "鏇存柊", value: "update" }, + { label: "瓒呮椂", value: "timeout" }, +] +// 鎵ц鐩戝惉鍣�: 浜嬩欢绫诲瀷 +export const EXECUTION_EVENT_TYPE = [ + { label: "寮�濮�", value: "start" }, + { label: "缁撴潫", value: "end" }, +] +// 浜嬩欢绫诲瀷: 瀹氭椂鍣ㄧ被鍨� +export const EVENT_DEFINITION_TYPE = [ + { label: "鏃�", value: "null" }, + { label: "鏃ユ湡", value: "date" }, + { label: "鎸佺画鏃堕暱", value: "duration" }, + { label: "寰幆", value: "cycle" }, +] +// 瀛楁閰嶇疆 +export const FIELD_TYPE = [ + { label: "瀛楃涓�", value: "string" }, + { label: "琛ㄨ揪寮�", value: "expression" }, +] + diff --git a/ruoyi-ui/apps/web-antd/src/package/penal/multi-instance/ElementMultiInstance.vue b/ruoyi-ui/apps/web-antd/src/package/penal/multi-instance/ElementMultiInstance.vue new file mode 100644 index 0000000..e13eaf5 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/penal/multi-instance/ElementMultiInstance.vue @@ -0,0 +1,331 @@ +<template> + <div class="panel-tab__content"> + <el-table :data="elementListenersList" size="small" border> + <el-table-column label="搴忓彿" width="50px" type="index" /> + <el-table-column label="浜嬩欢绫诲瀷" min-width="100px" prop="event" :formatter="formatterEvent" /> + <el-table-column label="鐩戝惉鍣ㄧ被鍨�" min-width="100px" show-overflow-tooltip :formatter="formatterListener" /> + <el-table-column label="鎿嶄綔" width="90px"> + <template v-slot="{ row, $index }"> + <el-button link type="primary" @click="openListenerForm(row, $index)">缂栬緫</el-button> + <el-button link type="danger" @click="removeListener(row, $index)">绉婚櫎</el-button> + </template> + </el-table-column> + </el-table> + <div class="element-drawer__button"> + <el-button size="small" type="primary" :icon="Plus" @click="openListenerForm(null)">娣诲姞鐩戝惉鍣�</el-button> + </div> + + <!-- 鐩戝惉鍣� 缂栬緫/鍒涘缓 閮ㄥ垎 --> + <el-drawer + v-model="listenerFormModelVisible" + title="鎵ц鐩戝惉鍣�" + :size="`${width}px`" + class="listener-drawer" + append-to-body + destroy-on-close> + <el-form :model="listenerForm" label-width="96px" ref="listenerFormRef" @submit.prevent> + <el-form-item label="浜嬩欢绫诲瀷" prop="event" :rules="{ required: true, trigger: ['blur', 'change'], message: '璇烽�夋嫨浜嬩欢绫诲瀷' }"> + <el-select v-model="listenerForm.event"> + <el-option v-for="item in EXECUTION_EVENT_TYPE" :key="item.value" :label="item.label" :value="item.value" /> + </el-select> + </el-form-item> + <el-form-item label="鐩戝惉鍣ㄧ被鍨�" prop="listenerType" :rules="{ required: true, trigger: ['blur', 'change'], message: '璇烽�夋嫨鐩戝惉鍣ㄧ被鍨�' }"> + <el-input + v-model="listenerForm[selectedProp]" + clearable + class="input-with-select"> + <template #prepend> + <el-select v-model="listenerForm.listenerType" style="width: 100%"> + <el-option v-for="item in LISTENER_TYPE" :key="item.key" :label="item.label" :value="item.value" /> + </el-select> + </template> + <template #append> + <el-button :icon="Search" /> + </template> + </el-input> + </el-form-item> + <template v-for="item in LISTENER_TYPEWithoutJB"> + <el-form-item + v-if="listenerForm.listenerType === item.value" + :key="item.key" + :label="item.label" + :prop="item.prop" + :rules="{ required: true, trigger: ['blur', 'change'] }" + > + <el-input v-model="listenerForm[item.prop]" clearable /> + </el-form-item> + </template> + <!-- 鑴氭湰绫诲瀷 --> + <!-- <template v-if="listenerForm.listenerType === LISTENER_TYPE[LISTENER_TYPE.length - 1].value">--> + <!-- <el-form-item--> + <!-- label="鑴氭湰鏍煎紡"--> + <!-- prop="scriptFormat"--> + <!-- key="listener-script-format"--> + <!-- :rules="{ required: true, trigger: ['blur', 'change'], message: '璇峰~鍐欒剼鏈牸寮�' }"--> + <!-- >--> + <!-- <el-input v-model="listenerForm.scriptFormat" clearable />--> + <!-- </el-form-item>--> + <!-- <el-form-item--> + <!-- label="鑴氭湰绫诲瀷"--> + <!-- prop="scriptType"--> + <!-- key="listener-script-type"--> + <!-- :rules="{ required: true, trigger: ['blur', 'change'], message: '璇烽�夋嫨鑴氭湰绫诲瀷' }"--> + <!-- >--> + <!-- <el-select v-model="listenerForm.scriptType">--> + <!-- <el-option v-for="item in SCRIPT_TYPE" :key="item.value" :label="item.label" :value="item.value" />--> + <!-- </el-select>--> + <!-- </el-form-item>--> + <!-- <el-form-item--> + <!-- v-if="listenerForm.scriptType === SCRIPT_TYPE[0].value"--> + <!-- label="鑴氭湰鍐呭"--> + <!-- prop="value"--> + <!-- key="listener-script"--> + <!-- :rules="{ required: true, trigger: ['blur', 'change'], message: '璇峰~鍐欒剼鏈唴瀹�' }"--> + <!-- >--> + <!-- <el-input v-model="listenerForm.value" clearable />--> + <!-- </el-form-item>--> + <!-- <el-form-item--> + <!-- v-if="listenerForm.scriptType === SCRIPT_TYPE[1].value"--> + <!-- label="璧勬簮鍦板潃"--> + <!-- prop="resource"--> + <!-- key="listener-resource"--> + <!-- :rules="{ required: true, trigger: ['blur', 'change'], message: '璇峰~鍐欒祫婧愬湴鍧�' }"--> + <!-- >--> + <!-- <el-input v-model="listenerForm.resource" clearable />--> + <!-- </el-form-item>--> + <!-- </template>--> + </el-form> + <el-divider /> + <div class="listener-filed__title"> + <span><el-icon><Menu /></el-icon>娉ㄥ叆瀛楁锛�</span> + <el-button size="small" type="primary" @click="openListenerFieldForm(null)">娣诲姞瀛楁</el-button> + </div> + <el-table :data="fieldsListOfListener" size="small" max-height="240" border fit style="flex: none"> + <el-table-column label="搴忓彿" width="50px" type="index" /> + <el-table-column label="瀛楁鍚嶇О" min-width="100px" prop="name" /> + <el-table-column label="瀛楁绫诲瀷" min-width="80px" show-overflow-tooltip :formatter="formatterField" /> + <el-table-column label="瀛楁鍊�/琛ㄨ揪寮�" min-width="100px" show-overflow-tooltip :formatter="row => row.string || row.expression" /> + <el-table-column label="鎿嶄綔" width="100px"> + <template v-slot="{ row, $index }"> + <el-button link type="primary" @click="openListenerFieldForm(row, $index)">缂栬緫</el-button> + <el-button link type="danger" @click="removeListenerField(row, $index)">绉婚櫎</el-button> + </template> + </el-table-column> + </el-table> + + <div class="element-drawer__button"> + <el-button size="small" @click="listenerFormModelVisible = false">鍙� 娑�</el-button> + <el-button size="small" type="primary" @click="saveListenerConfig">淇� 瀛�</el-button> + </div> + </el-drawer> + + <!-- 娉ㄥ叆瑗挎 缂栬緫/鍒涘缓 閮ㄥ垎 --> + <el-dialog title="瀛楁閰嶇疆" v-model="listenerFieldFormModelVisible" width="600px" append-to-body destroy-on-close> + <el-form :model="listenerFieldForm" size="small" label-width="96px" ref="listenerFieldFormRef" style="height: 136px" @submit.prevent> + <el-form-item label="瀛楁鍚嶇О锛�" prop="name" :rules="{ required: true, trigger: ['blur', 'change'], message: '璇峰~鍐欏瓧娈靛悕绉�' }"> + <el-input v-model="listenerFieldForm.name" clearable /> + </el-form-item> + <el-form-item label="瀛楁绫诲瀷锛�" prop="fieldType" :rules="{ required: true, trigger: ['blur', 'change'], message: '璇烽�夋嫨瀛楁绫诲瀷' }"> + <el-select v-model="listenerFieldForm.fieldType"> + <el-option v-for="item in FIELD_TYPE" :key="item.value" :label="item.label" :value="item.value" /> + </el-select> + </el-form-item> + <el-form-item + v-if="listenerFieldForm.fieldType === FIELD_TYPE[0].value" + label="瀛楁鍊硷細" + prop="string" + key="field-string" + :rules="{ required: true, trigger: ['blur', 'change'], message: '璇峰~鍐欏瓧娈靛��' }" + > + <el-input v-model="listenerFieldForm.string" clearable /> + </el-form-item> + <el-form-item + v-if="listenerFieldForm.fieldType === FIELD_TYPE[1].value" + label="琛ㄨ揪寮忥細" + prop="expression" + key="field-expression" + :rules="{ required: true, trigger: ['blur', 'change'], message: '璇峰~鍐欒〃杈惧紡' }" + > + <el-input v-model="listenerFieldForm.expression" clearable /> + </el-form-item> + </el-form> + <template v-slot:footer> + <el-button size="small" @click="listenerFieldFormModelVisible = false">鍙� 娑�</el-button> + <el-button size="small" type="primary" @click="saveListenerFiled">纭� 瀹�</el-button> + </template> + </el-dialog> + </div> +</template> +<script> +import { createListenerObject, updateElementExtensions } from "../../utils"; +import { + initListenerType, + initListenerForm, + EXECUTION_EVENT_TYPE, + LISTENER_TYPE, + FIELD_TYPE, + SCRIPT_TYPE, + EVENT_DEFINITION_TYPE +} from "./utilSelf"; +import { Plus, Search } from "@element-plus/icons-vue"; + +export default { + name: "ElementListeners", + setup() { + return { Plus, Search } + }, + props: { + id: String, + type: String + }, + inject: { + prefix: "prefix", + width: "width" + }, + data() { + return { + elementListenersList: [], // 鐩戝惉鍣ㄥ垪琛� + listenerForm: {}, // 鐩戝惉鍣ㄨ鎯呰〃鍗� + listenerFormModelVisible: false, // 鐩戝惉鍣� 缂栬緫 渚ц竟鏍忔樉绀虹姸鎬� + fieldsListOfListener: [], + listenerFieldForm: {}, // 鐩戝惉鍣� 娉ㄥ叆瀛楁 璇︽儏琛ㄥ崟 + listenerFieldFormModelVisible: false, // 鐩戝惉鍣� 娉ㄥ叆瀛楁琛ㄥ崟寮圭獥 鏄剧ず鐘舵�� + editingListenerIndex: -1, // 鐩戝惉鍣ㄦ墍鍦ㄤ笅鏍囷紝-1 涓烘柊澧� + editingListenerFieldIndex: -1, // 瀛楁鎵�鍦ㄤ笅鏍囷紝-1 涓烘柊澧� + }; + }, + watch: { + id: { + immediate: true, + handler(val) { + val && val.length && this.$nextTick(() => this.resetListenersList()); + } + } + }, + computed: { + EXECUTION_EVENT_TYPE() { return EXECUTION_EVENT_TYPE }, + LISTENER_TYPE() { return LISTENER_TYPE }, + FIELD_TYPE() { return FIELD_TYPE }, + SCRIPT_TYPE() { return SCRIPT_TYPE }, + EVENT_DEFINITION_TYPE() { return EVENT_DEFINITION_TYPE }, + formatterEvent() { + return (row) => { + return EXECUTION_EVENT_TYPE.find(find => find.value === row.event)?.label || "" + } + }, + formatterListener() { + return (row) => { + return LISTENER_TYPE.find(find => find.value === row.listener)?.label || "" + } + }, + formatterField() { + return (row) => { + return FIELD_TYPE.find(find => find.value === row.fieldType)?.label || "" + } + }, + selectedProp() { + return LISTENER_TYPE.find(find => find.value === this.listenerForm.listenerType)?.prop || '' + } + }, + methods: { + resetListenersList() { + this.bpmnElement = window.bpmnInstances.bpmnElement; + this.otherExtensionList = []; + this.bpmnElementListeners = + this.bpmnElement.businessObject?.extensionElements?.values?.filter(ex => ex.$type === `${this.prefix}:ExecutionListener`) ?? []; + this.elementListenersList = this.bpmnElementListeners.map(listener => initListenerType(listener)); + }, + // 鎵撳紑 鐩戝惉鍣ㄨ鎯� 渚ц竟鏍� + openListenerForm(listener, index) { + if (listener) { + this.listenerForm = initListenerForm(listener); + this.editingListenerIndex = index; + } else { + this.listenerForm = {}; + this.editingListenerIndex = -1; // 鏍囪涓烘柊澧� + } + if (listener && listener.fields) { + this.fieldsListOfListener = listener.fields.map(field => ({ + ...field, + fieldType: field.string ? "string" : "expression" + })); + } else { + this.fieldsListOfListener = []; + this.listenerForm["fields"] = [] + } + // 鎵撳紑渚ц竟鏍忓苟娓呮楠岃瘉鐘舵�� + this.listenerFormModelVisible = true; + this.$nextTick(() => { + if (this.$refs["listenerFormRef"]) this.$refs["listenerFormRef"].clearValidate(); + }); + }, + // 鎵撳紑鐩戝惉鍣ㄥ瓧娈电紪杈戝脊绐� + openListenerFieldForm(field, index) { + this.listenerFieldForm = field ? JSON.parse(JSON.stringify(field)) : {}; + this.editingListenerFieldIndex = field ? index : -1; + this.listenerFieldFormModelVisible = true; + this.$nextTick(() => { + if (this.$refs["listenerFieldFormRef"]) this.$refs["listenerFieldFormRef"].clearValidate(); + }); + }, + // 淇濆瓨鐩戝惉鍣ㄦ敞鍏ュ瓧娈� + async saveListenerFiled() { + let validateStatus = await this.$refs["listenerFieldFormRef"].validate(); + if (!validateStatus) return; // 楠岃瘉涓嶉�氳繃鐩存帴杩斿洖 + if (this.editingListenerFieldIndex === -1) { + this.fieldsListOfListener.push(this.listenerFieldForm); + this.listenerForm.fields.push(this.listenerFieldForm); + } else { + this.fieldsListOfListener.splice(this.editingListenerFieldIndex, 1, this.listenerFieldForm); + this.listenerForm.fields.splice(this.editingListenerFieldIndex, 1, this.listenerFieldForm); + } + this.listenerFieldFormModelVisible = false; + this.$nextTick(() => (this.listenerFieldForm = {})); + }, + // 绉婚櫎鐩戝惉鍣ㄥ瓧娈� + removeListenerField(field, index) { + this.$confirm("纭绉婚櫎璇ュ瓧娈靛悧锛�", "鎻愮ず", { + confirmButtonText: "纭� 璁�", + cancelButtonText: "鍙� 娑�" + }) + .then(() => { + this.fieldsListOfListener.splice(index, 1); + this.listenerForm.fields.splice(index, 1); + }) + .catch(() => console.info("鎿嶄綔鍙栨秷")); + }, + // 绉婚櫎鐩戝惉鍣� + removeListener(listener, index) { + this.$confirm("纭绉婚櫎璇ョ洃鍚櫒鍚楋紵", "鎻愮ず", { + confirmButtonText: "纭� 璁�", + cancelButtonText: "鍙� 娑�" + }) + .then(() => { + this.bpmnElementListeners.splice(index, 1); + this.elementListenersList.splice(index, 1); + updateElementExtensions(this.bpmnElement, this.otherExtensionList.concat(this.bpmnElementListeners)); + }) + .catch(() => console.info("鎿嶄綔鍙栨秷")); + }, + // 淇濆瓨鐩戝惉鍣ㄩ厤缃� + async saveListenerConfig() { + let validateStatus = await this.$refs["listenerFormRef"].validate(); + if (!validateStatus) return; // 楠岃瘉涓嶉�氳繃鐩存帴杩斿洖 + const listenerObject = createListenerObject(this.listenerForm, false, this.prefix); + if (this.editingListenerIndex === -1) { + this.bpmnElementListeners.push(listenerObject); + this.elementListenersList.push(this.listenerForm); + } else { + this.bpmnElementListeners.splice(this.editingListenerIndex, 1, listenerObject); + this.elementListenersList.splice(this.editingListenerIndex, 1, this.listenerForm); + } + // 淇濆瓨鍏朵粬閰嶇疆 + this.otherExtensionList = this.bpmnElement.businessObject?.extensionElements?.values?.filter(ex => ex.$type !== `${this.prefix}:ExecutionListener`) ?? []; + updateElementExtensions(this.bpmnElement, this.otherExtensionList.concat(this.bpmnElementListeners)); + // 4. 闅愯棌渚ц竟鏍� + this.listenerFormModelVisible = false; + this.listenerForm = {}; + } + } +}; +</script> diff --git a/ruoyi-ui/apps/web-antd/src/package/penal/multi-instance/utilSelf.js b/ruoyi-ui/apps/web-antd/src/package/penal/multi-instance/utilSelf.js new file mode 100644 index 0000000..a001b9c --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/penal/multi-instance/utilSelf.js @@ -0,0 +1,80 @@ +// 鍒濆鍖栬〃鍗曟暟鎹� +export function initListenerForm(listener) { + let self = { + ...listener + }; + if (listener.script) { + self = { + ...listener, + ...listener.script, + scriptType: listener.script.resource ? "externalScript" : "inlineScript" + }; + } + if (listener.event === "timeout" && listener.eventDefinitions) { + if (listener.eventDefinitions.length) { + let k = ""; + for (let key in listener.eventDefinitions[0]) { + console.log(listener.eventDefinitions, key); + if (key.indexOf("time") !== -1) { + k = key; + self.eventDefinitionType = key.replace("time", "").toLowerCase(); + } + } + console.log(k); + self.eventTimeDefinitions = listener.eventDefinitions[0][k].body; + } + } + return self; +} + +export function initListenerType(listener) { + let listenerType; + if (listener.class) listenerType = "classListener"; + if (listener.expression) listenerType = "expressionListener"; + if (listener.delegateExpression) listenerType = "delegateExpressionListener"; + if (listener.script) listenerType = "scriptListener"; + return { + ...JSON.parse(JSON.stringify(listener)), + ...(listener.script ?? {}), + listenerType: listenerType + }; +} + +// 鐩戝惉绫诲瀷 +export const LISTENER_TYPE = [ + { label: "Java 绫�", value: "classListener", prop: "class", key: "listener-class" }, + { label: "琛ㄨ揪寮�", value: "expressionListener", prop: "expression", key: "listener-expression" }, + { label: "浠g悊琛ㄨ揪寮�", value: "delegateExpressionListener", prop: "delegateExpression", key: "listener-delegate" }, + // { label: "鑴氭湰", value: "scriptListener", prop: "scriptFormat", key: "listener-script-format" }, +] +// 鑴氭湰绫诲瀷 +export const SCRIPT_TYPE = [ + { label: "鍐呰仈鑴氭湰", value: "inlineScript" }, + { label: "澶栭儴鑴氭湰", value: "externalScript" }, +] +// 浠诲姟鐩戝惉鍣�: 浜嬩欢绫诲瀷 +export const TASK_EVENT_TYPE = [ + { label: "鍒涘缓", value: "create" }, + { label: "鎸囨淳", value: "assignment" }, + { label: "瀹屾垚", value: "complete" }, + { label: "鍒犻櫎", value: "delete" }, + { label: "鏇存柊", value: "update" }, + { label: "瓒呮椂", value: "timeout" }, +] +// 鎵ц鐩戝惉鍣�: 浜嬩欢绫诲瀷 +export const EXECUTION_EVENT_TYPE = [ + { label: "寮�濮�", value: "start" }, + { label: "缁撴潫", value: "end" }, +] +// 浜嬩欢绫诲瀷: 瀹氭椂鍣ㄧ被鍨� +export const EVENT_DEFINITION_TYPE = [ + { label: "鏃�", value: "null" }, + { label: "鏃ユ湡", value: "date" }, + { label: "鎸佺画鏃堕暱", value: "duration" }, + { label: "寰幆", value: "cycle" }, +] +// 瀛楁閰嶇疆 +export const FIELD_TYPE = [ + { label: "瀛楃涓�", value: "string" }, + { label: "琛ㄨ揪寮�", value: "expression" }, +] diff --git a/ruoyi-ui/apps/web-antd/src/package/penal/other/ElementOtherConfig.vue b/ruoyi-ui/apps/web-antd/src/package/penal/other/ElementOtherConfig.vue new file mode 100644 index 0000000..7016a06 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/penal/other/ElementOtherConfig.vue @@ -0,0 +1,59 @@ +<template> + <div class="panel-tab__content"> + <div class="element-property input-property"> + <div class="element-property__label">鍏冪礌鏂囨。锛�</div> + <div class="element-property__value"> + <el-input + type="textarea" + v-model="documentation" + size="small" + resize="vertical" + :autosize="{ minRows: 2, maxRows: 4 }" + @input="updateDocumentation" + @blur="updateDocumentation" + /> + </div> + </div> + </div> +</template> + +<script> +export default { + name: "ElementOtherConfig", + props: { + id: String + }, + data() { + return { + documentation: "" + }; + }, + watch: { + id: { + immediate: true, + handler: function(id) { + if (id && id.length) { + this.$nextTick(() => { + const documentations = window.bpmnInstances.bpmnElement.businessObject?.documentation; + this.documentation = documentations && documentations.length ? documentations[0].text : ""; + }); + } else { + this.documentation = ""; + } + } + } + }, + methods: { + updateDocumentation() { + (this.bpmnElement && this.bpmnElement.id === this.id) || (this.bpmnElement = window.bpmnInstances.elementRegistry.get(this.id)); + const documentation = window.bpmnInstances.bpmnFactory.create("bpmn:Documentation", { text: this.documentation }); + window.bpmnInstances.modeling.updateProperties(this.bpmnElement, { + documentation: [documentation] + }); + } + }, + beforeUnmount() { + this.bpmnElement = null; + } +}; +</script> diff --git a/ruoyi-ui/apps/web-antd/src/package/penal/properties/ElementProperties.vue b/ruoyi-ui/apps/web-antd/src/package/penal/properties/ElementProperties.vue new file mode 100644 index 0000000..a8715b8 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/penal/properties/ElementProperties.vue @@ -0,0 +1,141 @@ +<template> + <div class="panel-tab__content"> + <el-table :data="elementPropertyList" size="small" max-height="240" border fit> + <el-table-column label="搴忓彿" width="50px" type="index" /> + <el-table-column label="灞炴�у悕" prop="name" min-width="100px" show-overflow-tooltip /> + <el-table-column label="灞炴�у��" prop="value" min-width="100px" show-overflow-tooltip /> + <el-table-column label="鎿嶄綔" width="90px"> + <template v-slot="{ row, $index }"> + <el-button link type="" @click="openAttributesForm(row, $index)">缂栬緫</el-button> + <el-divider direction="vertical" /> + <el-button link type="" style="color: #ff4d4f" @click="removeAttributes(row, $index)">绉婚櫎</el-button> + </template> + </el-table-column> + </el-table> + <div class="element-drawer__button"> + <el-button size="small" type="primary" :icon="Plus" @click="openAttributesForm(null, -1)">娣诲姞灞炴��</el-button> + </div> + + <el-dialog v-model="propertyFormModelVisible" title="灞炴�ч厤缃�" width="600px" append-to-body destroy-on-close> + <el-form :model="propertyForm" label-width="80px" size="small" ref="attributeFormRef" @submit.prevent> + <el-form-item label="灞炴�у悕锛�" prop="name"> + <el-input v-model="propertyForm.name" clearable /> + </el-form-item> + <el-form-item label="灞炴�у�硷細" prop="value"> + <el-input v-model="propertyForm.value" clearable /> + </el-form-item> + </el-form> + <template v-slot:footer> + <el-button size="small" @click="propertyFormModelVisible = false">鍙� 娑�</el-button> + <el-button size="small" type="primary" @click="saveAttribute">纭� 瀹�</el-button> + </template> + </el-dialog> + </div> +</template> + +<script> +import { Plus } from '@element-plus/icons-vue' +export default { + name: "ElementProperties", + setup() { + return { + Plus + } + }, + props: { + id: String, + type: String + }, + inject: { + prefix: "prefix", + width: "width" + }, + data() { + return { + elementPropertyList: [], + propertyForm: {}, + editingPropertyIndex: -1, + propertyFormModelVisible: false + }; + }, + watch: { + id: { + immediate: true, + handler(val) { + val && val.length && this.resetAttributesList(); + } + } + }, + methods: { + resetAttributesList() { + this.bpmnElement = window.bpmnInstances.bpmnElement; + this.otherExtensionList = []; // 鍏朵粬鎵╁睍閰嶇疆 + this.bpmnElementProperties = + this.bpmnElement.businessObject?.extensionElements?.values?.filter(ex => { + if (ex.$type !== `${this.prefix}:Properties`) { + this.otherExtensionList.push(ex); + } + return ex.$type === `${this.prefix}:Properties`; + }) ?? []; + + // 淇濆瓨鎵�鏈夌殑 鎵╁睍灞炴�у瓧娈� + this.bpmnElementPropertyList = this.bpmnElementProperties.reduce((pre, current) => pre.concat(current.values), []); + // 澶嶅埗 鏄剧ず + this.elementPropertyList = JSON.parse(JSON.stringify(this.bpmnElementPropertyList ?? [])); + }, + openAttributesForm(attr, index) { + this.editingPropertyIndex = index; + this.propertyForm = index === -1 ? {} : JSON.parse(JSON.stringify(attr)); + this.propertyFormModelVisible = true; + this.$nextTick(() => { + if (this.$refs["attributeFormRef"]) this.$refs["attributeFormRef"].clearValidate(); + }); + }, + removeAttributes(attr, index) { + this.$confirm("纭绉婚櫎璇ュ睘鎬у悧锛�", "鎻愮ず", { + confirmButtonText: "纭� 璁�", + cancelButtonText: "鍙� 娑�" + }) + .then(() => { + this.elementPropertyList.splice(index, 1); + this.bpmnElementPropertyList.splice(index, 1); + // 鏂板缓涓�涓睘鎬у瓧娈电殑淇濆瓨鍒楄〃 + const propertiesObject = window.bpmnInstances.moddle.create(`${this.prefix}:Properties`, { + values: this.bpmnElementPropertyList + }); + this.updateElementExtensions(propertiesObject); + this.resetAttributesList(); + }) + .catch(() => console.info("鎿嶄綔鍙栨秷")); + }, + saveAttribute() { + const { name, value } = this.propertyForm; + console.log(this.bpmnElementPropertyList); + if (this.editingPropertyIndex !== -1) { + window.bpmnInstances.modeling.updateModdleProperties(this.bpmnElement, this.bpmnElementPropertyList[this.editingPropertyIndex], { + name, + value + }); + } else { + // 鏂板缓灞炴�у瓧娈� + const newPropertyObject = window.bpmnInstances.moddle.create(`${this.prefix}:Property`, { name, value }); + // 鏂板缓涓�涓睘鎬у瓧娈电殑淇濆瓨鍒楄〃 + const propertiesObject = window.bpmnInstances.moddle.create(`${this.prefix}:Properties`, { + values: this.bpmnElementPropertyList.concat([newPropertyObject]) + }); + this.updateElementExtensions(propertiesObject); + } + this.propertyFormModelVisible = false; + this.resetAttributesList(); + }, + updateElementExtensions(properties) { + const extensions = window.bpmnInstances.moddle.create("bpmn:ExtensionElements", { + values: this.otherExtensionList.concat([properties]) + }); + window.bpmnInstances.modeling.updateProperties(this.bpmnElement, { + extensionElements: extensions + }); + } + } +}; +</script> diff --git a/ruoyi-ui/apps/web-antd/src/package/penal/signal-message/SignalAndMessage.vue b/ruoyi-ui/apps/web-antd/src/package/penal/signal-message/SignalAndMessage.vue new file mode 100644 index 0000000..663788e --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/penal/signal-message/SignalAndMessage.vue @@ -0,0 +1,110 @@ +<template> + <div class="panel-tab__content"> + <div class="panel-tab__content--title"> + <span><el-icon style="margin-right: 8px; color: #555555"><Menu /></el-icon>娑堟伅鍒楄〃</span> + <el-button size="small" type="primary" :icon="Plus" @click="openModel('message')">鍒涘缓鏂版秷鎭�</el-button> + </div> + <el-table :data="messageList" size="small" border> + <el-table-column type="index" label="搴忓彿" width="60px" /> + <el-table-column label="娑堟伅ID" prop="id" max-width="300px" show-overflow-tooltip /> + <el-table-column label="娑堟伅鍚嶇О" prop="name" max-width="300px" show-overflow-tooltip /> + </el-table> + <div class="panel-tab__content--title" style="padding-top: 8px; margin-top: 8px; border-top: 1px solid #eeeeee"> + <span><el-icon style="margin-right: 8px; color: #555555"><Menu /></el-icon>淇″彿鍒楄〃</span> + <el-button size="small" type="primary" :icon="Plus" @click="openModel('signal')">鍒涘缓鏂颁俊鍙�</el-button> + </div> + <el-table :data="signalList" size="small" border> + <el-table-column type="index" label="搴忓彿" width="60px" /> + <el-table-column label="淇″彿ID" prop="id" max-width="300px" show-overflow-tooltip /> + <el-table-column label="淇″彿鍚嶇О" prop="name" max-width="300px" show-overflow-tooltip /> + </el-table> + + <el-dialog v-model="modelVisible" :title="modelConfig.title" :close-on-click-modal="false" width="400px" append-to-body destroy-on-close> + <el-form :model="modelObjectForm" size="small" label-width="90px" @submit.prevent> + <el-form-item :label="modelConfig.idLabel"> + <el-input v-model="modelObjectForm.id" clearable /> + </el-form-item> + <el-form-item :label="modelConfig.nameLabel"> + <el-input v-model="modelObjectForm.name" clearable /> + </el-form-item> + </el-form> + <template v-slot:footer> + <el-button size="small" @click="modelVisible = false">鍙� 娑�</el-button> + <el-button size="small" type="primary" @click="addNewObject">淇� 瀛�</el-button> + </template> + </el-dialog> + </div> +</template> +<script> +import { Plus } from '@element-plus/icons-vue' +export default { + name: 'SignalAndMassage', + setup() { + return { + Plus + } + }, + data() { + return { + signalList: [], + messageList: [], + modelVisible: false, + modelType: '', + modelObjectForm: {} + }; + }, + computed: { + modelConfig() { + if (this.modelType === 'message') { + return { title: '鍒涘缓娑堟伅', idLabel: '娑堟伅ID', nameLabel: '娑堟伅鍚嶇О' }; + } else { + return { title: '鍒涘缓淇″彿', idLabel: '淇″彿ID', nameLabel: '淇″彿鍚嶇О' }; + } + } + }, + mounted() { + this.initDataList(); + }, + methods: { + initDataList() { + this.rootElements = window.bpmnInstances.modeler.getDefinitions().rootElements; + this.messageIdMap = {}; + this.signalIdMap = {}; + this.messageList = []; + this.signalList = []; + this.rootElements.forEach(el => { + if (el.$type === 'bpmn:Message') { + this.messageIdMap[el.id] = true; + this.messageList.push({ ...el }); + } + if (el.$type === 'bpmn:Signal') { + this.signalIdMap[el.id] = true; + this.signalList.push({ ...el }); + } + }); + }, + openModel(type) { + this.modelType = type; + this.modelObjectForm = {}; + this.modelVisible = true; + }, + addNewObject() { + if (this.modelType === 'message') { + if (this.messageIdMap[this.modelObjectForm.id]) { + return this.$message.error('璇ユ秷鎭凡瀛樺湪锛岃淇敼id鍚庨噸鏂颁繚瀛�'); + } + const messageRef = window.bpmnInstances.moddle.create('bpmn:Message', this.modelObjectForm); + this.rootElements.push(messageRef); + } else { + if (this.signalIdMap[this.modelObjectForm.id]) { + return this.$message.error('璇ヤ俊鍙峰凡瀛樺湪锛岃淇敼id鍚庨噸鏂颁繚瀛�'); + } + const signalRef = window.bpmnInstances.moddle.create('bpmn:Signal', this.modelObjectForm); + this.rootElements.push(signalRef); + } + this.modelVisible = false; + this.initDataList(); + } + } +}; +</script> diff --git a/ruoyi-ui/apps/web-antd/src/package/penal/task/ElementTask.vue b/ruoyi-ui/apps/web-antd/src/package/penal/task/ElementTask.vue new file mode 100644 index 0000000..b651f4b --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/penal/task/ElementTask.vue @@ -0,0 +1,72 @@ +<template> + <div class="panel-tab__content"> + <el-form size="small" label-width="90px" @submit.prevent> +<!-- <el-form-item label="寮傛寤剁画">--> +<!-- <el-checkbox v-model="taskConfigForm.asyncBefore" label="寮傛鍓�" @change="changeTaskAsync" />--> +<!-- <el-checkbox v-model="taskConfigForm.asyncAfter" label="寮傛鍚�" @change="changeTaskAsync" />--> +<!-- <el-checkbox v-model="taskConfigForm.exclusive" v-if="taskConfigForm.asyncAfter || taskConfigForm.asyncBefore" label="鎺掗櫎" @change="changeTaskAsync" />--> +<!-- </el-form-item>--> + <component :is="witchTaskComponent" v-bind="$props" /> + </el-form> + </div> +</template> + +<script> +import UserTask from "./task-components/UserTask.vue"; +import ScriptTask from "./task-components/ScriptTask.vue"; +import ReceiveTask from "./task-components/ReceiveTask.vue"; + +export default { + name: "ElementTaskConfig", + components: { UserTask, ScriptTask, ReceiveTask }, + props: { + id: String, + type: String + }, + data() { + return { + taskConfigForm: { + asyncAfter: false, + asyncBefore: false, + exclusive: false + }, + witchTaskComponent: "", + installedComponent: { + // 鎵嬪伐浠诲姟涓庢櫘閫氫换鍔′竴鑷达紝涓嶉渶瑕佸叾浠栭厤缃� + // 鎺ユ敹娑堟伅浠诲姟锛岄渶瑕佸湪鍏ㄥ眬涓嬫彃鍏ユ柊鐨勬秷鎭疄渚嬶紝骞跺湪璇ヨ妭鐐逛笅鐨� messageRef 灞炴�х粦瀹氳瀹炰緥 + // 鍙戦�佷换鍔°�佹湇鍔′换鍔°�佷笟鍔¤鍒欎换鍔″叡鐢ㄤ竴涓浉鍚岄厤缃� + UserTask: "UserTask", // 鐢ㄦ埛浠诲姟閰嶇疆 + ScriptTask: "ScriptTask", // 鑴氭湰浠诲姟閰嶇疆 + ReceiveTask: "ReceiveTask" // 娑堟伅鎺ユ敹浠诲姟 + } + }; + }, + watch: { + id: { + immediate: true, + handler() { + this.bpmnElement = window.bpmnInstances.bpmnElement; + // this.taskConfigForm.asyncBefore = this.bpmnElement?.businessObject?.asyncBefore; + // this.taskConfigForm.asyncAfter = this.bpmnElement?.businessObject?.asyncAfter; + // this.taskConfigForm.exclusive = this.bpmnElement?.businessObject?.exclusive; + } + }, + type: { + immediate: true, + handler() { + this.witchTaskComponent = this.installedComponent[this.$props.type]; + } + } + }, + methods: { + changeTaskAsync() { + if (!this.taskConfigForm.asyncBefore && !this.taskConfigForm.asyncAfter) { + this.taskConfigForm.exclusive = false; + } + window.bpmnInstances.modeling.updateProperties(window.bpmnInstances.bpmnElement, { + ...this.taskConfigForm + }); + } + } +}; +</script> diff --git a/ruoyi-ui/apps/web-antd/src/package/penal/task/task-components/ReceiveTask.vue b/ruoyi-ui/apps/web-antd/src/package/penal/task/task-components/ReceiveTask.vue new file mode 100644 index 0000000..c40cbe3 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/penal/task/task-components/ReceiveTask.vue @@ -0,0 +1,102 @@ +<template> + <div style="margin-top: 16px"> + <el-form-item label="娑堟伅瀹炰緥"> + <div style="display: flex; align-items: center; justify-content: space-between; flex-wrap: nowrap"> + <el-select v-model="bindMessageId" @change="updateTaskMessage"> + <el-option v-for="id in Object.keys(messageMap)" :value="id" :label="messageMap[id]" :key="id" /> + </el-select> + <el-button size="small" type="primary" :icon="Plus" style="margin-left: 8px" @click="openMessageModel" /> + </div> + </el-form-item> + <el-dialog v-model="messageModelVisible" :close-on-click-modal="false" title="鍒涘缓鏂版秷鎭�" width="400px" append-to-body destroy-on-close> + <el-form :model="newMessageForm" size="small" label-width="90px" @submit.prevent> + <el-form-item label="娑堟伅ID"> + <el-input v-model="newMessageForm.id" clearable /> + </el-form-item> + <el-form-item label="娑堟伅鍚嶇О"> + <el-input v-model="newMessageForm.name" clearable /> + </el-form-item> + </el-form> + <template v-slot:footer> + <el-button size="small" type="primary" @click="createNewMessage">纭� 璁�</el-button> + </template> + </el-dialog> + </div> +</template> + +<script> +export default { + name: "ReceiveTask", + setup() { + return { + Plus + } + }, + props: { + id: String, + type: String + }, + data() { + return { + bindMessageId: "", + newMessageForm: {}, + messageMap: {}, + messageModelVisible: false + }; + }, + watch: { + id: { + immediate: true, + handler() { + this.$nextTick(() => this.getBindMessage()); + } + } + }, + created() { + this.bpmnMessageRefsMap = Object.create(null); + this.bpmnRootElements = window.bpmnInstances.modeler.getDefinitions().rootElements; + this.bpmnRootElements + .filter(el => el.$type === "bpmn:Message") + .forEach(m => { + this.bpmnMessageRefsMap[m.id] = m; + this.messageMap[m.id] = m.name + }); + this.messageMap["-1"] = "鏃�" // 娣诲姞涓�涓┖瀵硅薄锛屼繚璇佸彲浠ュ彇娑堝師娑堟伅缁戝畾 + }, + methods: { + getBindMessage() { + this.bpmnElement = window.bpmnInstances.bpmnElement; + this.bindMessageId = this.bpmnElement.businessObject?.messageRef?.id || "-1"; + }, + openMessageModel() { + this.messageModelVisible = true; + this.newMessageForm = {}; + }, + createNewMessage() { + if (this.messageMap[this.newMessageForm.id]) { + this.$message.error("璇ユ秷鎭凡瀛樺湪锛岃淇敼id鍚庨噸鏂颁繚瀛�"); + return; + } + const newMessage = window.bpmnInstances.moddle.create("bpmn:Message", this.newMessageForm); + this.bpmnRootElements.push(newMessage); + this.messageMap[this.newMessageForm.id] = this.newMessageForm.name + this.bpmnMessageRefsMap[this.newMessageForm.id] = newMessage; + this.messageModelVisible = false; + }, + updateTaskMessage(messageId) { + if (messageId === "-1") { + window.bpmnInstances.modeling.updateProperties(this.bpmnElement, { + messageRef: null + }); + } else { + window.bpmnInstances.modeling.updateProperties(this.bpmnElement, { + messageRef: this.bpmnMessageRefsMap[messageId] + }); + } + } + }, + beforeUnmount() { + this.bpmnElement = null; + } +}; +</script> diff --git a/ruoyi-ui/apps/web-antd/src/package/penal/task/task-components/ScriptTask.vue b/ruoyi-ui/apps/web-antd/src/package/penal/task/task-components/ScriptTask.vue new file mode 100644 index 0000000..2239219 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/penal/task/task-components/ScriptTask.vue @@ -0,0 +1,85 @@ +<template> + <div style="margin-top: 16px"> + <el-form-item label="鑴氭湰鏍煎紡"> + <el-input v-model="scriptTaskForm.scriptFormat" clearable @input="updateElementTask()" @change="updateElementTask()" /> + </el-form-item> + <el-form-item label="鑴氭湰绫诲瀷"> + <el-select v-model="scriptTaskForm.scriptType"> + <el-option label="鍐呰仈鑴氭湰" value="inline" /> + <el-option label="澶栭儴璧勬簮" value="external" /> + </el-select> + </el-form-item> + <el-form-item label="鑴氭湰" v-show="scriptTaskForm.scriptType === 'inline'"> + <el-input + v-model="scriptTaskForm.script" + type="textarea" + resize="vertical" + :autosize="{ minRows: 2, maxRows: 4 }" + clearable + @input="updateElementTask()" + @change="updateElementTask()" + /> + </el-form-item> + <el-form-item label="璧勬簮鍦板潃" v-show="scriptTaskForm.scriptType === 'external'"> + <el-input v-model="scriptTaskForm.resource" clearable @input="updateElementTask()" @change="updateElementTask()" /> + </el-form-item> + <el-form-item label="缁撴灉鍙橀噺"> + <el-input v-model="scriptTaskForm.resultVariable" clearable @input="updateElementTask()" @change="updateElementTask()" /> + </el-form-item> + </div> +</template> + +<script> +export default { + name: "ScriptTask", + props: { + id: String, + type: String + }, + data() { + return { + defaultTaskForm: { + scriptFormat: "", + script: "", + resource: "", + resultVariable: "" + }, + scriptTaskForm: {} + }; + }, + watch: { + id: { + immediate: true, + handler() { + this.bpmnElement = window.bpmnInstances.bpmnElement; + this.$nextTick(() => this.resetTaskForm()); + } + } + }, + methods: { + resetTaskForm() { + for (let key in this.defaultTaskForm) { + let value = this.bpmnElement?.businessObject[key] || this.defaultTaskForm[key]; + this.scriptTaskForm[key] = value + } + this.scriptTaskForm["scriptType"] = this.scriptTaskForm.script ? "inline" : "external" + }, + updateElementTask() { + let taskAttr = Object.create(null); + taskAttr.scriptFormat = this.scriptTaskForm.scriptFormat || null; + taskAttr.resultVariable = this.scriptTaskForm.resultVariable || null; + if (this.scriptTaskForm.scriptType === "inline") { + taskAttr.script = this.scriptTaskForm.script || null; + taskAttr.resource = null; + } else { + taskAttr.resource = this.scriptTaskForm.resource || null; + taskAttr.script = null; + } + window.bpmnInstances.modeling.updateProperties(this.bpmnElement, taskAttr); + } + }, + beforeUnmount() { + this.bpmnElement = null; + } +}; +</script> diff --git a/ruoyi-ui/apps/web-antd/src/package/penal/task/task-components/UserTask.vue b/ruoyi-ui/apps/web-antd/src/package/penal/task/task-components/UserTask.vue new file mode 100644 index 0000000..98b379b --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/penal/task/task-components/UserTask.vue @@ -0,0 +1,684 @@ +<template> + <div style="margin-top: 16px"> + <el-row> + <h4 class="mb-10 fw-700">瀹℃壒浜鸿缃�</h4> + <el-radio-group v-model="dataType" @change="changeDataType"> + <el-radio + v-for="item in USER_TASK_GROUP" + :key="item.value" + :label="item.value" + >{{item.label}}</el-radio> + </el-radio-group> + </el-row> + <el-row> + <el-col :span="24" v-if="dataType === USER_TASK_GROUP[0].value"> + <el-tag v-for="userText in selectedUser.text" :key="userText" effect="plain" closable @close="handleClose(userText)"> + {{userText}} + </el-tag> + <div class="element-drawer__button"> + <el-button size="default" type="primary" :icon="Plus" @click="onSelectUsers()">娣诲姞鐢ㄦ埛</el-button> + </div> + </el-col> + <el-col :span="24" v-else-if="dataType === USER_TASK_GROUP[1].value"> + <el-select v-model="roleIds" multiple size="default" placeholder="璇烽�夋嫨瑙掕壊" @change="changeSelectRoles" style="width: 100%"> + <el-option + v-for="item in roleOptions" + :key="item.roleId" + :label="item.roleName" + :value="`ROLE${item.roleId}`" + :disabled="item.status === 1"> + </el-option> + </el-select> + </el-col> + <el-col :span="24" v-else-if="dataType === USER_TASK_GROUP[2].value"> + <el-tree-select + v-model="deptIds" + :data="deptTreeData" + :props="deptProps" + value-key="id" + 聽placeholder="閫夋嫨閮ㄩ棬" + check-strictly + clearable + @clear="clearDept" + ref="selectTree" + @node-click="checkedDeptChangeNode" + /> + + + </el-col> + </el-row> + <el-row> + <el-col :span="24" v-show="showMultiFlog"> + <el-divider /> + <h4><b>澶氬疄渚嬪鎵规柟寮�</b></h4> + <el-row> + <el-radio-group v-model="multiLoopType" @change="changeMultiLoopType()" class="flex-column radio-group-flex"> + <el-row v-for="item in MULTI_INSTANCE_TYPE" :key="item.value"> + <el-radio :label="item.value">{{item.label}}</el-radio> + </el-row> + </el-radio-group> + </el-row> + <el-row v-if="multiLoopType !== MULTI_INSTANCE_TYPE[0].value"> + <el-tooltip content="寮�鍚悗锛屽疄渚嬮渶鎸夐『搴忚疆娴佸鎵�" placement="top-start" @click.stop.prevent> + <el-icon><InfoFilled /></el-icon> + </el-tooltip> + <span class="custom-label">椤哄簭瀹℃壒锛�</span> + <el-switch v-model="isSequential" @change="changeMultiLoopType()" /> + </el-row> + </el-col> + </el-row> + + <!-- 鍊欓�夌敤鎴峰脊绐� --> + <el-dialog title="鍊欓�夌敤鎴�" v-model="userOpen" width="60%" append-to-body> + <el-row type="flex" :gutter="20"> + <!--閮ㄩ棬鏁版嵁--> + <el-col :span="7"> + <el-card shadow="never" style="height: 100%"> + <div class="head-container"> + <el-input + v-model="queryParamsDeptIds" + placeholder="璇疯緭鍏ラ儴闂ㄥ悕绉�" + clearable + size="small" + :prefix-icon="Search" + style="margin-bottom: 20px" + /> + <el-tree + :data="deptOptions" + :props="deptProps" + :expand-on-click-node="false" + :filter-node-method="filterNode" + ref="deptTree" + :default-expand-all="false" + node-key="id" + highlight-current + @current-change="handleNodeClick" + /> + </div> + </el-card> + </el-col> + <el-col :span="17"> + <el-form + :model="queryParams" + ref="queryForm" + size="small" + :inline="true" + label-width="68px" + > + <el-form-item label="鐢ㄦ埛鍚嶇О" prop="nickName"> + <el-input + v-model="queryParams.nickName" + placeholder="璇疯緭鍏ョ敤鎴峰悕绉�" + clearable + style="width: 150px" + @keyup.enter.native="handleQuery" + /> + </el-form-item> + <el-form-item> + <el-button + type="primary" + :icon="Search" + @click="handleQuery" + >鎼滅储</el-button> + <el-button + :icon="Refresh" + @click="resetQuery" + >閲嶇疆</el-button> + </el-form-item> + </el-form> + <el-table + ref="multipleTable" + height="600" + v-loading="tableLoading" + :data="userTableList" + border + @row-click="clickRow" + @selection-change="handleSelectionChange"> + <el-table-column type="selection" width="50" align="center" /> + <el-table-column label="鐢ㄦ埛鍚�" align="center" prop="nickName" /> + <el-table-column label="閮ㄩ棬" align="center" prop="dept.deptName" /> + </el-table> + <pagination + :total="userTotal" + v-model:page="queryParams.pageNum" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + </el-col> + </el-row> + <template #footer class="dialog-footer"> + <el-button type="primary" size="default" @click="handleTaskUserComplete">纭� 瀹�</el-button> + <el-button size="default" @click="userOpen = false">鍙� 娑�</el-button> + </template> + </el-dialog> + </div> +</template> +<script> +import { userList, getDeptTree } from "#/api/system/user"; +import { roleList } from "#/api/system/role"; +import { uniqBy } from 'lodash' + +const USER_TASK_GROUP = [ + { + label: "鎸囧畾鐢ㄦ埛", + value: "USERS", + }, + { + label: "瑙掕壊", + value: "ROLES", + }, + { + label: "閮ㄩ棬", + value: "DEPTS", + }, + { + label: "鍙戣捣浜�", + value: "INITIATOR", + }, +] +const USER_TASK_FORM = { + dataType: '', + assignee: '', + candidateUsers: '', + candidateGroups: '', + text: '', + deptId: '', + // dueDate: '', + // followUpDate: '', + // priority: '' +} +const MULTI_INSTANCE_TYPE = [ + { label: "鏃�", value: 'Null' }, + { label: "浼氱锛堥渶鎵�鏈夊鎵逛汉鍚屾剰锛�", value: 'SequentialMultiInstance' }, + { label: "鎴栫锛堜竴鍚嶅鎵逛汉鍚屾剰鍗冲彲锛�", value: 'ParallelMultiInstance' }, + { label: "鑷畾涔�", value: 'DiyMultiInstance' }, +] +export default { + name: "UserTask", + props: { + id: String, + type: String + }, + setup() { + + return { Plus, Search, Refresh } + }, + data() { + return { + tableLoading: false, + dataType: USER_TASK_GROUP[0].value, + selectedUser: { + ids: [], + text: [] + }, + userOpen: false, + deptName: undefined, + deptOptions: [], //閮ㄩ棬鍒楄〃 + deptProps: { + children: "children", + label: "label", + value: 'id' + }, + userTableList: [], + userTotal: 0, + selectedUserDate: [], + roleOptions: [], + roleIds: [], + deptTreeData: [], + deptIds: [], + queryParamsDeptIds:[], + // 鏌ヨ鍙傛暟 + queryParams: { + deptId: undefined, + nickName: undefined, + pageNum: 1, + pageSize: 20, + }, + showMultiFlog: false, + isSequential: false, + multiLoopType: MULTI_INSTANCE_TYPE[0].value, + }; + }, + watch: { + id: { + immediate: true, + handler() { + this.bpmnElement = window.bpmnInstances.bpmnElement; + this.$nextTick(() => this.resetTaskForm()); + } + }, + // 鏍规嵁鍚嶇О绛涢�夐儴闂ㄦ爲 + queryParamsDeptIds(val) { + this.$nextTick(() => { + this.$refs.deptTree.filter(val); + }) + } + }, + computed: { + USER_TASK_GROUP() { return USER_TASK_GROUP }, + MULTI_INSTANCE_TYPE() { return MULTI_INSTANCE_TYPE } + }, + methods: { + // 鍒濆鍖栧彉閲� + async resetTaskForm() { + const bpmnElementObj = this.bpmnElement?.businessObject; + if (!bpmnElementObj) { + return; + } + this.clearOptionsData() + this.dataType = bpmnElementObj.dataType; + if (this.dataType === USER_TASK_GROUP[0].value) { + let userIdData = bpmnElementObj['candidateUsers'] || bpmnElementObj['assignee']; + let userText = bpmnElementObj.text || []; + if (userIdData && userIdData.toString().length > 0 && userText && userText.length > 0) { + this.selectedUser.ids = userIdData.toString().split(','); + this.selectedUser.text = userText.split(','); + } + if (this.selectedUser.ids.length > 1) { + this.showMultiFlog = true; + } + } else if (this.dataType === USER_TASK_GROUP[1].value) { + // 鑾峰彇瑙掕壊鍒楄〃 + this.getRoleOptions(); + let roleIdData = bpmnElementObj['candidateGroups'] || []; + if (roleIdData && roleIdData.length > 0) { + this.roleIds = roleIdData.split(',') + } + this.showMultiFlog = true; + } else if (this.dataType === USER_TASK_GROUP[2].value) { + // 鑾峰彇閮ㄩ棬鍒楄〃 + await this.getDeptTreeData(); + let deptIdData = bpmnElementObj['candidateGroups'] || []; + if (deptIdData && deptIdData.length > 0) { + this.deptIds = deptIdData.split(','); + // 鍥炴樉 + this.checkedDeptChange(this.deptIds); + } + this.showMultiFlog = true; + } + this.getElementLoop(bpmnElementObj); + }, + // 娓呯┖閫夐」鏁版嵁 + clearOptionsData() { + this.selectedUser.ids = []; + this.selectedUser.text = []; + this.roleIds = []; + this.deptIds = []; + }, + // 鏇存柊鑺傜偣鏁版嵁 + updateElementTask() { + const taskObj = Object.assign({}, { ...USER_TASK_FORM }) + window.bpmnInstances.modeling.updateProperties(this.bpmnElement, taskObj); + }, + // 鏌ヨ閮ㄩ棬涓嬫媺鏍戠粨鏋� + getDeptOptions() { + return new Promise((resolve, reject) => { + if (!this.deptOptions || this.deptOptions.length <= 0) { + // 閫夋嫨閮ㄩ棬鏍� 鏁版嵁绗竴娆¤姹� + getDeptTree().then(response => { + this.deptOptions = [].concat( + [{ id: '', label: '鍏ㄩ儴', children: [] }], + [...response.data] + ); + this.handleNodeClick(this.deptOptions[0]) + resolve() + }) + } else { + // 纭繚閫夋嫨閮ㄩ棬鏍戝凡鍒濆鍖� 浣嗕粛璇锋眰琛ㄦ牸鏁版嵁 澶嶉�夋鍥炴樉 + this.handleNodeClick(this.deptOptions[0]) + } + }); + }, + // 鏌ヨ閮ㄩ棬涓嬫媺鏍戠粨鏋勶紙鍚儴闂ㄥ墠缂�锛� + getDeptTreeData() { + function refactorTree(data) { + return data.map(node => { + let treeData = { id: `DEPT${node.id}`, label: node.label, parentId: node.parentId, weight: node.weight }; + if (node.children && node.children.length > 0) { + treeData.children = refactorTree(node.children); + } + return treeData; + }); + } + return new Promise((resolve, reject) => { + if (!this.deptTreeData || this.deptTreeData.length <= 0) { + getDeptTree().then((res) => { + this.deptTreeData = refactorTree(res.data); + resolve() + }).catch(() => { + reject() + }) + } else { + resolve() + } + }) + }, + // 鏌ヨ閮ㄩ棬涓嬫媺鏍戠粨鏋� + getRoleOptions() { + if (!this.roleOptions || this.roleOptions.length <= 0) { + roleList().then(response => this.roleOptions = response.rows); + } + }, + // 鏌ヨ鐢ㄦ埛鍒楄〃 + getList() { + this.tableLoading = true + userList({ ...this.queryParams }).then(response => { + this.userTableList = response.rows; + this.userTotal = response.total; + // this.$nextTick(() => { + // setTimeout(() => { + // this.selectedUserDate.forEach(each => { + // const findObj = this.userTableList.find(find => find.userId === each.userId) || {} + // if(Object.keys(findObj).length > 0) { + // this.$refs.multipleTable.toggleRowSelection(findObj) + // } + // }) + // }, 500) + // }) + }).finally(() => { + this.tableLoading = false + }) + }, + /** 鎼滅储鎸夐挳鎿嶄綔 */ + handleQuery() { + this.queryParams.pageNum = 1; + this.getList(); + }, + /** 閲嶇疆鎸夐挳鎿嶄綔 */ + resetQuery() { + this.$refs.queryForm.resetFields(); + this.$refs.queryForm.clearValidate(); + this.queryParams = { + deptId: '', + nickName: undefined, + pageSize: 20 + }; + this.handleQuery(); + }, + // 绛涢�夎妭鐐� + filterNode(value, data) { + if (!value) return true; + return data.label.indexOf(value) !== -1; + }, + // 鑺傜偣鍗曞嚮浜嬩欢 + handleNodeClick(data) { + this.queryParams.deptId = data.id; + this.handleQuery(); + }, + // 鍏抽棴鏍囩 + handleClose(tag) { + this.selectedUserDate.splice(this.selectedUserDate.indexOf(tag), 1); + this.handleTaskUserChange() + }, + /** + * 閫夋嫨琛� + */ + clickRow(row) { + this.$refs.multipleTable.toggleRowSelection(row); + }, + // 澶氶�夋閫変腑鏁版嵁 + handleSelectionChange(selection) { + if(selection.length) { + const concatData = [].concat(selection, this.selectedUserDate); + this.selectedUserDate = uniqBy(concatData, 'userId'); + } + }, + // 娣诲姞鐢ㄦ埛 + onSelectUsers() { + this.$nextTick(()=> + { + setTimeout(() => { + this.$refs.multipleTable.clearSelection(),100 + }) + }) + this.getDeptOptions() + this.userOpen = true; + + }, + // 娣诲姞鐢ㄦ埛-纭鎸夐挳 + handleTaskUserComplete() { + if (!this.selectedUserDate || this.selectedUserDate.length <= 0) { + this.$modal.msgError('璇烽�夋嫨鐢ㄦ埛'); + return; + } + USER_TASK_FORM.dataType = USER_TASK_GROUP[0].value; + this.handleTaskUserChange() + this.changeMultiLoopType(); + this.userOpen = false; + }, + // 鎸囧畾鐢ㄦ埛鏀瑰彉纭畾 + handleTaskUserChange() { + this.selectedUser.text = this.selectedUserDate.map(item => item.nickName) || []; + if (this.selectedUserDate.length === 1) { + let data = this.selectedUserDate[0]; + USER_TASK_FORM.assignee = data.userId; + USER_TASK_FORM.text = data.nickName; + USER_TASK_FORM.candidateUsers = null; + this.showMultiFlog = false; + this.multiLoopType = MULTI_INSTANCE_TYPE[0].value; + } else { + USER_TASK_FORM.candidateUsers = this.selectedUserDate.map(item => item.userId).join() || null; + USER_TASK_FORM.text = this.selectedUserDate.map(item => item.nickName).join() || null; + USER_TASK_FORM.assignee = null; + this.showMultiFlog = true; + } + this.selectedUserDate=[]; + this.updateElementTask(); + }, + // 閫変腑瑙掕壊 + changeSelectRoles(roleArray) { + let groups = null; + let text = null; + if (roleArray && roleArray.length > 0) { + USER_TASK_FORM.dataType = USER_TASK_GROUP[1].value; + groups = roleArray.join() || null; + let textArr = this.roleOptions.filter(k => roleArray.indexOf(`ROLE${k.roleId}`) >= 0); + text = textArr?.map(k => k.roleName).join() || null; + } else { + USER_TASK_FORM.dataType = null; + this.multiLoopType = MULTI_INSTANCE_TYPE[0].value; + } + USER_TASK_FORM.candidateGroups = groups; + USER_TASK_FORM.text = text; + this.updateElementTask(); + this.changeMultiLoopType(); + }, + + + checkedDeptChangeNode(node){ + this.checkedDeptChange([node.id]); + }, + clearDept(){ + USER_TASK_FORM.candidateGroups = ""; + USER_TASK_FORM.text = ""; + USER_TASK_FORM.assignee =""; + USER_TASK_FORM.candidateUsers =""; + this.updateElementTask(); + this.multiLoopType = MULTI_INSTANCE_TYPE[0].value; + this.changeMultiLoopType(); + }, + // 閫変腑閮ㄩ棬 + checkedDeptChange(deptArray) { + let groups = null; + let text = null; + this.deptIds = deptArray[0]; + if (deptArray && deptArray.length > 0) { + USER_TASK_FORM.dataType = USER_TASK_GROUP[2].value; + groups = deptArray.join(',') || null; + let treeStarkData = JSON.parse(JSON.stringify(this.deptTreeData)); + const filterData = this.filterTreeData(treeStarkData, deptArray); + text = filterData.map(item => item.label).join(',') || null + } else { + USER_TASK_FORM.dataType = null; + this.multiLoopType = MULTI_INSTANCE_TYPE[0].value; + } + USER_TASK_FORM.candidateGroups = groups; + USER_TASK_FORM.text = text; + this.updateElementTask(); + this.changeMultiLoopType(); + }, + filterTreeData (data, deptArray) { + const result = []; + const traverse = (items) => { + for (const item of items) { + // 妫�鏌ュ綋鍓嶈妭鐐规槸鍚﹀湪 deptArray 涓� + if (deptArray.includes(item.id)) { + result.push(item); // 濡傛灉鍦紝娣诲姞鍒扮粨鏋滄暟缁勪腑 + } + + // 濡傛灉鏈� children锛岄�掑綊澶勭悊 + if (item.children) { + traverse(item.children); + } + } + }; + traverse(data); // 寮�濮嬮亶鍘� + return result; // 杩斿洖鎵佸钩鍖栫殑缁撴灉鏁扮粍 + }, + + // 淇敼瀹℃壒浜虹被鍨� + changeDataType(val) { + if (val === USER_TASK_GROUP[1].value || val === USER_TASK_GROUP[2].value || (val === USER_TASK_GROUP[0].value && this.selectedUser.ids.length > 1)) { + this.showMultiFlog = true; + } else { + this.showMultiFlog = false; + } + this.multiLoopType = MULTI_INSTANCE_TYPE[0].value; + this.changeMultiLoopType(); + // 娓呯┖ USER_TASK_FORM 鎵�鏈夊睘鎬у�� + Object.keys(USER_TASK_FORM).forEach(key => USER_TASK_FORM[key] = null); + USER_TASK_FORM.dataType = val; + if (val === USER_TASK_GROUP[0].value) { + if (this.selectedUser && this.selectedUser.ids && this.selectedUser.ids.length > 0) { + if (this.selectedUser.ids.length === 1) { + USER_TASK_FORM.assignee = this.selectedUser.ids[0]; + } else { + USER_TASK_FORM.candidateUsers = this.selectedUser.ids.join() + } + USER_TASK_FORM.text = this.selectedUser.text?.join() || null + } + } else if (val === USER_TASK_GROUP[1].value) { + this.getRoleOptions(); + if (this.roleIds && this.roleIds.length > 0) { + USER_TASK_FORM.candidateGroups = this.roleIds.join() || null; + let textArr = this.roleOptions.filter(k => this.roleIds.indexOf(`ROLE${k.roleId}`) >= 0); + USER_TASK_FORM.text = textArr?.map(k => k.roleName).join() || null; + } + } else if (val === USER_TASK_GROUP[2].value) { + this.getDeptTreeData(); + if (this.deptIds && this.deptIds.length > 0) { + USER_TASK_FORM.candidateGroups = this.deptIds.join() || null; + let textArr = [] + let treeStarkData = JSON.parse(JSON.stringify(this.deptTreeData)); + this.deptIds.forEach(id => { + let stark = [] + stark = stark.concat(treeStarkData); + while(stark.length) { + let temp = stark.shift(); + if(temp.children) { + stark = temp.children.concat(stark); + } + if(id === temp.id) { + textArr.push(temp); + } + } + }) + USER_TASK_FORM.text = textArr?.map(k => k.label).join() || null; + } + } else { + USER_TASK_FORM.assignee = "${initiator}"; + USER_TASK_FORM.text = "娴佺▼鍙戣捣浜�"; + } + this.updateElementTask(); + }, + // 鍒濆鍖栧瀹炰緥鍙橀噺 + getElementLoop(businessObject) { + if (!businessObject.loopCharacteristics) { + this.multiLoopType = MULTI_INSTANCE_TYPE[0].value; + return; + } + this.isSequential = businessObject.loopCharacteristics.isSequential; + if (businessObject.loopCharacteristics.completionCondition) { + if (businessObject.loopCharacteristics.completionCondition.body === "${nrOfCompletedInstances >= nrOfInstances}") { + this.multiLoopType = MULTI_INSTANCE_TYPE[1].value; // 浼氱 + } else if (businessObject.loopCharacteristics.completionCondition.body === "${nrOfCompletedInstances >= 0}") { + this.multiLoopType = MULTI_INSTANCE_TYPE[2].value; // 鎴栫 + } else { + this.multiLoopType = MULTI_INSTANCE_TYPE[3].value; // 鑷畾涔� + } + } + }, + // 鏇存柊澶氬疄渚嬪彉閲� + changeMultiLoopType() { + // 鍙栨秷澶氬疄渚嬮厤缃� + if (this.multiLoopType === MULTI_INSTANCE_TYPE[0].value) { + window.bpmnInstances.modeling.updateProperties(this.bpmnElement, { loopCharacteristics: null, candidateUsers: null }); + return; + } + + this.multiLoopInstance = window.bpmnInstances.moddle.create("bpmn:MultiInstanceLoopCharacteristics", { isSequential: this.isSequential }); + // 鏇存柊澶氬疄渚嬮厤缃� + window.bpmnInstances.modeling.updateProperties(this.bpmnElement, { + loopCharacteristics: this.multiLoopInstance, + assignee: '${assignee}' + }); + // 璁剧疆瀹℃壒浜� + USER_TASK_FORM.assignee = '${assignee}'; + // 瀹屾垚鏉′欢 + let completionCondition = null; + switch (this.multiLoopType) { + case MULTI_INSTANCE_TYPE[1].value: + completionCondition = window.bpmnInstances.moddle.create("bpmn:FormalExpression", { body: "${nrOfCompletedInstances >= nrOfInstances}" }); + break; + case MULTI_INSTANCE_TYPE[2].value: + completionCondition = window.bpmnInstances.moddle.create("bpmn:FormalExpression", { body: "${nrOfCompletedInstances > 0}" }); + break; + default: + completionCondition = window.bpmnInstances.moddle.create("bpmn:FormalExpression", { body: "" }); + break; + } + // 鏇存柊妯″潡灞炴�т俊鎭� + window.bpmnInstances.modeling.updateModdleProperties(this.bpmnElement, this.multiLoopInstance, { + collection: MULTI_INSTANCE_TYPE[1].value || MULTI_INSTANCE_TYPE[2].value ? '${multiInstanceHandler.getUserIds(execution)}' : "", + elementVariable: MULTI_INSTANCE_TYPE[1].value || MULTI_INSTANCE_TYPE[2].value ? 'assignee' : "", + completionCondition + }); + }, + }, + beforeDestroy() { + this.bpmnElement = null; + }, +}; +</script> +<style scoped lang="scss"> +.el-row .el-radio-group { + margin-bottom: 15px; + .el-radio { + line-height: 28px; + } +} +.el-tag { + margin-bottom: 10px; + + .el-tag { + margin-left: 10px; + } +} + +.custom-label { + padding-left: 5px; + font-weight: 500; + font-size: 14px; + color: #606266; +} + +.head-container { + height: 480px; + overflow-y: auto; +} +.el-row .radio-group-flex { + margin: 15px 0 0 0; + justify-items: flex-start; + align-items: flex-start !important; +} +</style> + diff --git a/ruoyi-ui/apps/web-antd/src/package/theme/element-variables.scss b/ruoyi-ui/apps/web-antd/src/package/theme/element-variables.scss new file mode 100644 index 0000000..02f74f0 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/theme/element-variables.scss @@ -0,0 +1,70 @@ +/* 鏀瑰彉涓婚鑹插彉閲� */ +$--color-primary: #1890ff; +$--color-danger: #ff4d4f; + +/* 鏀瑰彉 icon 瀛椾綋璺緞鍙橀噺锛屽繀闇� */ +// $--font-path: '~element-ui/lib/theme-chalk/fonts'; + +// @import "~element-ui/packages/theme-chalk/src/index"; + +.el-table td, +.el-table th { + color: #333; +} +.el-drawer__header { + padding: 16px 16px 8px 16px; + margin: 0; + line-height: 24px; + font-size: 18px; + color: #303133; + box-sizing: border-box; + border-bottom: 1px solid #e8e8e8; +} +div[class^="el-drawer"]:focus, +span:focus { + outline: none; +} +.el-drawer__body { + box-sizing: border-box; + padding: 16px; + width: 100%; + overflow-y: auto; +} + +.el-dialog { + margin-top: 50vh !important; + transform: translateY(-50%); + overflow: hidden; +} +.el-dialog__wrapper { + overflow: hidden; + max-height: 100vh; +} +.el-dialog__header { + padding: 16px 16px 8px 16px; + box-sizing: border-box; + border-bottom: 1px solid #e8e8e8; +} +.el-dialog__body { + padding: 16px; + max-height: 80vh; + box-sizing: border-box; + overflow-y: auto; +} +.el-dialog__footer { + padding: 16px; + box-sizing: border-box; + border-top: 1px solid #e8e8e8; +} +.el-dialog__close { + font-weight: 600; +} +.el-select { + width: 100%; +} +.el-divider:not(.el-divider--horizontal) { + margin: 0 8px ; +} +.el-divider.el-divider--horizontal { + margin: 16px 0; +} diff --git a/ruoyi-ui/apps/web-antd/src/package/theme/index.scss b/ruoyi-ui/apps/web-antd/src/package/theme/index.scss new file mode 100644 index 0000000..93e7c09 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/theme/index.scss @@ -0,0 +1,168 @@ +@use "bpmn-js-token-simulation/assets/css/bpmn-js-token-simulation.css"; +@use "bpmn-js/dist/assets/diagram-js.css"; +@use "bpmn-js/dist/assets/bpmn-font/css/bpmn.css"; +@use "bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css"; +@use "./process-designer.scss"; +@use "./process-panel.scss"; + +$success-color: #4eb819; +$primary-color: #409EFF; +$warning-color: #E6A23C; +$danger-color: #F56C6C; +$cancel-color: #909399; + +.process-viewer { + position: relative; + border: 1px solid #EFEFEF; + background: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PHBhdHRlcm4gaWQ9ImEiIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgcGF0dGVyblVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHBhdGggZD0iTTAgMTBoNDBNMTAgMHY0ME0wIDIwaDQwTTIwIDB2NDBNMCAzMGg0ME0zMCAwdjQwIiBmaWxsPSJub25lIiBzdHJva2U9IiNlMGUwZTAiIG9wYWNpdHk9Ii4yIi8+PHBhdGggZD0iTTQwIDBIMHY0MCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZTBlMGUwIi8+PC9wYXR0ZXJuPjwvZGVmcz48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2EpIi8+PC9zdmc+') repeat!important; + + .success-arrow { + fill: $success-color; + stroke: $success-color; + } + + .success-conditional { + fill: white; + stroke: $success-color; + } + + .fail-arrow { + fill: $warning-color; + stroke: $warning-color; + } + + .fail-conditional { + fill: white; + stroke: $warning-color; + } + + .success.djs-connection { + .djs-visual path { + stroke: $success-color!important; + marker-end: url(#sequenceflow-end-white-success)!important; + } + } + + .success.djs-connection.condition-expression { + .djs-visual path { + marker-start: url(#conditional-flow-marker-white-success)!important; + } + } + + .success.djs-shape { + .djs-visual rect { + stroke: $success-color!important; + fill: $success-color!important; + fill-opacity: 0.15!important; + } + + .djs-visual polygon { + stroke: $success-color!important; + } + + .djs-visual path:nth-child(2) { + stroke: $success-color!important; + fill: $success-color!important; + } + + .djs-visual circle { + stroke: $success-color!important; + fill: $success-color!important; + fill-opacity: 0.15!important; + } + } + + .primary.djs-shape { + .djs-visual rect { + stroke: $primary-color!important; + fill: $primary-color!important; + fill-opacity: 0.15!important; + } + + .djs-visual polygon { + stroke: $primary-color!important; + } + + .djs-visual circle { + stroke: $primary-color!important; + fill: $primary-color!important; + fill-opacity: 0.15!important; + } + } + + .warning.djs-connection { + .djs-visual path { + stroke: $warning-color!important; + marker-end: url(#sequenceflow-end-white-fail)!important; + } + } + + .warning.djs-connection.condition-expression { + .djs-visual path { + marker-start: url(#conditional-flow-marker-white-fail)!important; + } + } + + .warning.djs-shape { + .djs-visual rect { + stroke: $warning-color!important; + fill: $warning-color!important; + fill-opacity: 0.15!important; + } + + .djs-visual polygon { + stroke: $warning-color!important; + } + + .djs-visual path:nth-child(2) { + stroke: $warning-color!important; + fill: $warning-color!important; + } + + .djs-visual circle { + stroke: $warning-color!important; + fill: $warning-color!important; + fill-opacity: 0.15!important; + } + } + + .danger.djs-shape { + .djs-visual rect { + stroke: $danger-color!important; + fill: $danger-color!important; + fill-opacity: 0.15!important; + } + + .djs-visual polygon { + stroke: $danger-color!important; + } + + .djs-visual circle { + stroke: $danger-color!important; + fill: $danger-color!important; + fill-opacity: 0.15!important; + } + } + + .cancel.djs-shape { + .djs-visual rect { + stroke: $cancel-color!important; + fill: $cancel-color!important; + fill-opacity: 0.15!important; + } + + .djs-visual polygon { + stroke: $cancel-color!important; + } + + .djs-visual circle { + stroke: $cancel-color!important; + fill: $cancel-color!important; + fill-opacity: 0.15!important; + } + } +} + +.process-viewer .djs-tooltip-container, .process-viewer .djs-overlay-container, .process-viewer .djs-palette { + display: none; +} diff --git a/ruoyi-ui/apps/web-antd/src/package/theme/process-designer.scss b/ruoyi-ui/apps/web-antd/src/package/theme/process-designer.scss new file mode 100644 index 0000000..386a153 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/theme/process-designer.scss @@ -0,0 +1,155 @@ +@import "bpmn-js-token-simulation/assets/css/bpmn-js-token-simulation.css"; +@import "diagram-js-minimap/assets/diagram-js-minimap.css"; + +// 杈规琚� token-simulation 鏍峰紡瑕嗙洊浜� +.djs-palette { + background: var(--palette-background-color); + border: solid 1px var(--palette-border-color) !important; + border-radius: 2px; +} + +.my-process-designer { + display: flex; + flex-direction: column; + width: 100%; + height: 100%; + box-sizing: border-box; + .my-process-designer__header { + width: 100%; + min-height: 36px; + .el-button { + text-align: center; + } + .el-button-group { + margin: 4px; + } + .el-tooltip__popper { + .el-button { + width: 100%; + text-align: left; + padding-left: 8px; + padding-right: 8px; + } + .el-button:hover { + background: rgba(64, 158, 255, 0.8); + color: #ffffff; + } + } + .align { + position: relative; + i { + &:after { + content: "|"; + position: absolute; + transform: rotate(90deg) translate(200%, -10%); + } + } + } + .align.align-left i { + transform: rotate(90deg); + } + .align.align-right i { + transform: rotate(-90deg); + } + .align.align-top i { + transform: rotate(180deg); + } + .align.align-bottom i { + transform: rotate(0deg); + } + .align.align-center i { + transform: rotate(90deg); + &:after { + transform: rotate(90deg) translate(0, -10%); + } + } + .align.align-middle i { + transform: rotate(0deg); + &:after { + transform: rotate(90deg) translate(0, -10%); + } + } + } + .my-process-designer__container { + display: inline-flex; + width: 100%; + flex: 1; + .my-process-designer__canvas { + flex: 1; + height: 100%; + position: relative; + background: url("data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PHBhdHRlcm4gaWQ9ImEiIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgcGF0dGVyblVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHBhdGggZD0iTTAgMTBoNDBNMTAgMHY0ME0wIDIwaDQwTTIwIDB2NDBNMCAzMGg0ME0zMCAwdjQwIiBmaWxsPSJub25lIiBzdHJva2U9IiNlMGUwZTAiIG9wYWNpdHk9Ii4yIi8+PHBhdGggZD0iTTQwIDBIMHY0MCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZTBlMGUwIi8+PC9wYXR0ZXJuPjwvZGVmcz48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2EpIi8+PC9zdmc+") + repeat !important; + div.toggle-mode { + display: none; + } + } + .my-process-designer__property-panel { + height: 100%; + overflow: scroll; + overflow-y: auto; + z-index: 10; + * { + box-sizing: border-box; + } + } + svg { + width: 100%; + height: 100%; + min-height: 100%; + overflow: hidden; + } + } +} + +//渚ц竟鏍忛厤缃� +.djs-palette.open { + .djs-palette-entries { + div[class^="bpmn-icon-"]:before, + div[class*="bpmn-icon-"]:before { + line-height: unset; + } + div.entry { + position: relative; + } + div.entry:hover { + &::after { + width: max-content; + content: attr(title); + vertical-align: text-bottom; + position: absolute; + right: -10px; + top: 0; + bottom: 0; + overflow: hidden; + transform: translateX(100%); + font-size: 0.5em; + display: inline-block; + text-decoration: inherit; + font-variant: normal; + text-transform: none; + background: #fafafa; + box-shadow: 0 0 6px #eeeeee; + border: 1px solid #cccccc; + box-sizing: border-box; + padding: 0 16px; + border-radius: 4px; + z-index: 100; + } + } + } +} +pre { + margin: 0; + height: 100%; + overflow: hidden; + max-height: calc(80vh - 32px); + overflow-y: auto; +} +.hljs { + word-break: break-word; + white-space: pre-wrap; +} +.hljs * { + font-family: Consolas, Monaco, monospace; +} diff --git a/ruoyi-ui/apps/web-antd/src/package/theme/process-panel.scss b/ruoyi-ui/apps/web-antd/src/package/theme/process-panel.scss new file mode 100644 index 0000000..8d72afb --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/theme/process-panel.scss @@ -0,0 +1,129 @@ +.process-panel__container { + box-sizing: border-box; + padding: 0 8px; + border-left: 1px solid #eeeeee; + box-shadow: 0 0 8px #cccccc; + max-height: 100%; + overflow-y: scroll; +} +.panel-tab__title { + font-weight: 600; + padding: 0 8px; + font-size: 1.1em; + line-height: 1.2em; + i { + margin-right: 8px; + font-size: 1.2em; + } +} +.panel-tab__content { + width: 100%; + box-sizing: border-box; + border-top: 1px solid #eeeeee; + padding: 8px 16px; + .panel-tab__content--title { + display: flex; + justify-content: space-between; + padding-bottom: 8px; + span { + flex: 1; + text-align: left; + } + } +} +.element-property { + width: 100%; + display: flex; + align-items: flex-start; + margin: 8px 0; + .element-property__label { + display: block; + width: 90px; + text-align: right; + overflow: hidden; + padding-right: 12px; + line-height: 32px; + font-size: 14px; + box-sizing: border-box; + } + .element-property__value { + flex: 1; + line-height: 32px; + } + .el-form-item { + width: 100%; + margin-bottom: 0; + padding-bottom: 18px; + } +} +.list-property { + flex-direction: column; + .element-listener-item { + width: 100%; + display: inline-grid; + grid-template-columns: 16px auto 32px 32px; + grid-column-gap: 8px; + } + .element-listener-item + .element-listener-item { + margin-top: 8px; + } +} +.listener-drawer { + + .el-drawer__header { + margin: 0PX; + padding: 10PX; + border-bottom: 1px solid #eeeeee; + .el-drawer__title { + font-size: 18px + } + } + + .el-drawer__body { + .el-form-item__label { + font-size: 14PX; + font-weight: 500; + } + .input-with-select .el-input-group__prepend { + background-color: var(--el-fill-color-blank); + padding: 0; + } + } +} +.listener-filed__title { + display: inline-flex; + width: 100%; + justify-content: space-between; + align-items: center; + margin-top: 0; + span { + // width: 200px; + text-align: left; + font-size: 14px; + } + i { + margin-right: 8px; + } +} +.element-drawer__button { + margin-top: 8px; + width: 100%; + display: inline-flex; + justify-content: space-around; +} +.element-drawer__button > .el-button { + width: 100%; +} + +.el-collapse-item__content { + padding-bottom: 0; +} +.el-input.is-disabled .el-input__inner { + color: #999999; +} +.el-form-item.el-form-item--mini { + margin-bottom: 0; + & + .el-form-item { + margin-top: 16px; + } +} diff --git a/ruoyi-ui/apps/web-antd/src/package/utils.js b/ruoyi-ui/apps/web-antd/src/package/utils.js new file mode 100644 index 0000000..bd61ef6 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/package/utils.js @@ -0,0 +1,71 @@ +// 鍒涘缓鐩戝惉鍣ㄥ疄渚� +export function createListenerObject(options, isTask, prefix) { + const listenerObj = Object.create(null); + listenerObj.event = options.event; + isTask && (listenerObj.id = options.id); // 浠诲姟鐩戝惉鍣ㄧ壒鏈夌殑 id 瀛楁 + switch (options.listenerType) { + case 'scriptListener': + listenerObj.script = createScriptObject(options, prefix); + break; + case 'expressionListener': + listenerObj.expression = options.expression; + break; + case 'delegateExpressionListener': + listenerObj.delegateExpression = options.delegateExpression; + break; + default: + listenerObj.class = options.class; + } + // 娉ㄥ叆瀛楁 + if (options.fields) { + listenerObj.fields = options.fields.map(field => { + return createFieldObject(field, prefix); + }); + } + // 浠诲姟鐩戝惉鍣ㄧ殑 瀹氭椂鍣� 璁剧疆 + if (isTask && options.event === 'timeout' && !!options.eventDefinitionType) { + const timeDefinition = window.bpmnInstances.moddle.create('bpmn:FormalExpression', { + body: options.eventTimeDefinitions + }); + const TimerEventDefinition = window.bpmnInstances.moddle.create('bpmn:TimerEventDefinition', { + id: `TimerEventDefinition_${uuid(8)}`, + [`time${options.eventDefinitionType.replace(/^\S/, s => s.toUpperCase())}`]: timeDefinition + }); + listenerObj.eventDefinitions = [TimerEventDefinition]; + } + return window.bpmnInstances.moddle.create(`${prefix}:${isTask ? 'TaskListener' : 'ExecutionListener'}`, listenerObj); +} + +// 鍒涘缓 鐩戝惉鍣ㄧ殑娉ㄥ叆瀛楁 瀹炰緥 +export function createFieldObject(option, prefix) { + const { name, fieldType, string, expression } = option; + const fieldConfig = fieldType === 'string' ? { name, string } : { name, expression }; + return window.bpmnInstances.moddle.create(`${prefix}:Field`, fieldConfig); +} + +// 鍒涘缓鑴氭湰瀹炰緥 +export function createScriptObject(options, prefix) { + const { scriptType, scriptFormat, value, resource } = options; + const scriptConfig = scriptType === 'inlineScript' ? { scriptFormat, value } : { scriptFormat, resource }; + return window.bpmnInstances.moddle.create(`${prefix}:Script`, scriptConfig); +} + +// 鏇存柊鍏冪礌鎵╁睍灞炴�� +export function updateElementExtensions(element, extensionList) { + const extensions = window.bpmnInstances.moddle.create('bpmn:ExtensionElements', { + values: extensionList + }); + window.bpmnInstances.modeling.updateProperties(element, { + extensionElements: extensions + }); +} + +// 鍒涘缓涓�涓猧d +export function uuid(length = 8, chars) { + let result = ''; + const charsString = chars || '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + for (let i = length; i > 0; --i) { + result += charsString[Math.floor(Math.random() * charsString.length)]; + } + return result; +} diff --git a/ruoyi-ui/apps/web-antd/src/utils/min-dash.js b/ruoyi-ui/apps/web-antd/src/utils/min-dash.js new file mode 100644 index 0000000..ab8b4e0 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/utils/min-dash.js @@ -0,0 +1,694 @@ +/* eslint-disable no-func-assign */ +/** + * Flatten array, one level deep. + * + * @param {Array<?>} arr + * + * @return {Array<?>} + */ +function flatten(arr) { + return Array.prototype.concat.apply([], arr); +} + +var nativeToString = Object.prototype.toString; +var nativeHasOwnProperty = Object.prototype.hasOwnProperty; +function isUndefined(obj) { + return obj === undefined; +} +function isDefined(obj) { + return obj !== undefined; +} +function isNil(obj) { + return obj == null; +} +function isArray(obj) { + return nativeToString.call(obj) === '[object Array]'; +} +function isObject(obj) { + return nativeToString.call(obj) === '[object Object]'; +} +function isNumber(obj) { + return nativeToString.call(obj) === '[object Number]'; +} +function isFunction(obj) { + var tag = nativeToString.call(obj); + return tag === '[object Function]' || tag === '[object AsyncFunction]' || tag === '[object GeneratorFunction]' || tag === '[object AsyncGeneratorFunction]' || tag === '[object Proxy]'; +} +function isString(obj) { + return nativeToString.call(obj) === '[object String]'; +} +/** + * Ensure collection is an array. + * + * @param {Object} obj + */ + +function ensureArray(obj) { + if (isArray(obj)) { + return; + } + + throw new Error('must supply array'); +} +/** + * Return true, if target owns a property with the given key. + * + * @param {Object} target + * @param {String} key + * + * @return {Boolean} + */ + +function has(target, key) { + return nativeHasOwnProperty.call(target, key); +} + +/** + * Find element in collection. + * + * @param {Array|Object} collection + * @param {Function|Object} matcher + * + * @return {Object} + */ + +function find(collection, matcher) { + matcher = toMatcher(matcher); + var match; + forEach(collection, function(val, key) { + if (matcher(val, key)) { + match = val; + return false; + } + }); + return match; +} +/** + * Find element index in collection. + * + * @param {Array|Object} collection + * @param {Function} matcher + * + * @return {Object} + */ + +function findIndex(collection, matcher) { + matcher = toMatcher(matcher); + var idx = isArray(collection) ? -1 : undefined; + forEach(collection, function(val, key) { + if (matcher(val, key)) { + idx = key; + return false; + } + }); + return idx; +} +/** + * Find element in collection. + * + * @param {Array|Object} collection + * @param {Function} matcher + * + * @return {Array} result + */ + +function filter(collection, matcher) { + var result = []; + forEach(collection, function(val, key) { + if (matcher(val, key)) { + result.push(val); + } + }); + return result; +} +/** + * Iterate over collection; returning something + * (non-undefined) will stop iteration. + * + * @param {Array|Object} collection + * @param {Function} iterator + * + * @return {Object} return result that stopped the iteration + */ + +function forEach(collection, iterator) { + var val, result; + + if (isUndefined(collection)) { + return; + } + + var convertKey = isArray(collection) ? toNum : identity; + + for (var key in collection) { + if (has(collection, key)) { + val = collection[key]; + result = iterator(val, convertKey(key)); + + if (result === false) { + return val; + } + } + } +} +/** + * Return collection without element. + * + * @param {Array} arr + * @param {Function} matcher + * + * @return {Array} + */ + +function without(arr, matcher) { + if (isUndefined(arr)) { + return []; + } + + ensureArray(arr); + matcher = toMatcher(matcher); + return arr.filter(function(el, idx) { + return !matcher(el, idx); + }); +} +/** + * Reduce collection, returning a single result. + * + * @param {Object|Array} collection + * @param {Function} iterator + * @param {Any} result + * + * @return {Any} result returned from last iterator + */ + +function reduce(collection, iterator, result) { + forEach(collection, function(value, idx) { + result = iterator(result, value, idx); + }); + return result; +} +/** + * Return true if every element in the collection + * matches the criteria. + * + * @param {Object|Array} collection + * @param {Function} matcher + * + * @return {Boolean} + */ + +function every(collection, matcher) { + return !!reduce(collection, function(matches, val, key) { + return matches && matcher(val, key); + }, true); +} +/** + * Return true if some elements in the collection + * match the criteria. + * + * @param {Object|Array} collection + * @param {Function} matcher + * + * @return {Boolean} + */ + +function some(collection, matcher) { + return !!find(collection, matcher); +} +/** + * Transform a collection into another collection + * by piping each member through the given fn. + * + * @param {Object|Array} collection + * @param {Function} fn + * + * @return {Array} transformed collection + */ + +function map(collection, fn) { + var result = []; + forEach(collection, function(val, key) { + result.push(fn(val, key)); + }); + return result; +} +/** + * Get the collections keys. + * + * @param {Object|Array} collection + * + * @return {Array} + */ + +function keys(collection) { + return collection && Object.keys(collection) || []; +} +/** + * Shorthand for `keys(o).length`. + * + * @param {Object|Array} collection + * + * @return {Number} + */ + +function size(collection) { + return keys(collection).length; +} +/** + * Get the values in the collection. + * + * @param {Object|Array} collection + * + * @return {Array} + */ + +function values(collection) { + return map(collection, function(val) { + return val; + }); +} +/** + * Group collection members by attribute. + * + * @param {Object|Array} collection + * @param {Function} extractor + * + * @return {Object} map with { attrValue => [ a, b, c ] } + */ + +function groupBy(collection, extractor) { + var grouped = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; + extractor = toExtractor(extractor); + forEach(collection, function(val) { + var discriminator = extractor(val) || '_'; + var group = grouped[discriminator]; + + if (!group) { + group = grouped[discriminator] = []; + } + + group.push(val); + }); + return grouped; +} +function uniqueBy(extractor) { + extractor = toExtractor(extractor); + var grouped = {}; + + for (var _len = arguments.length, collections = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + collections[_key - 1] = arguments[_key]; + } + + forEach(collections, function(c) { + return groupBy(c, extractor, grouped); + }); + var result = map(grouped, function(val, key) { + return val[0]; + }); + return result; +} +var unionBy = uniqueBy; +/** + * Sort collection by criteria. + * + * @param {Object|Array} collection + * @param {String|Function} extractor + * + * @return {Array} + */ + +function sortBy(collection, extractor) { + extractor = toExtractor(extractor); + var sorted = []; + forEach(collection, function(value, key) { + var disc = extractor(value, key); + var entry = { + d: disc, + v: value + }; + + for (var idx = 0; idx < sorted.length; idx++) { + var d = sorted[idx].d; + + if (disc < d) { + sorted.splice(idx, 0, entry); + return; + } + } // not inserted, append (!) + + sorted.push(entry); + }); + return map(sorted, function(e) { + return e.v; + }); +} +/** + * Create an object pattern matcher. + * + * @example + * + * const matcher = matchPattern({ id: 1 }); + * + * let element = find(elements, matcher); + * + * @param {Object} pattern + * + * @return {Function} matcherFn + */ + +function matchPattern(pattern) { + return function(el) { + return every(pattern, function(val, key) { + return el[key] === val; + }); + }; +} + +function toExtractor(extractor) { + return isFunction(extractor) ? extractor : function(e) { + return e[extractor]; + }; +} + +function toMatcher(matcher) { + return isFunction(matcher) ? matcher : function(e) { + return e === matcher; + }; +} + +function identity(arg) { + return arg; +} + +function toNum(arg) { + return Number(arg); +} + +/** + * Debounce fn, calling it only once if the given time + * elapsed between calls. + * + * Lodash-style the function exposes methods to `#clear` + * and `#flush` to control internal behavior. + * + * @param {Function} fn + * @param {Number} timeout + * + * @return {Function} debounced function + */ +function debounce(fn, timeout) { + var timer; + var lastArgs; + var lastThis; + var lastNow; + + function fire(force) { + var now = Date.now(); + var scheduledDiff = force ? 0 : lastNow + timeout - now; + + if (scheduledDiff > 0) { + return schedule(scheduledDiff); + } + + fn.apply(lastThis, lastArgs); + clear(); + } + + function schedule(timeout) { + timer = setTimeout(fire, timeout); + } + + function clear() { + if (timer) { + clearTimeout(timer); + } + + timer = lastNow = lastArgs = lastThis = undefined; + } + + function flush() { + if (timer) { + fire(true); + } + + clear(); + } + + function callback() { + lastNow = Date.now(); + + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + lastArgs = args; + lastThis = this; // ensure an execution is scheduled + + if (!timer) { + schedule(timeout); + } + } + + callback.flush = flush; + callback.cancel = clear; + return callback; +} +/** + * Throttle fn, calling at most once + * in the given interval. + * + * @param {Function} fn + * @param {Number} interval + * + * @return {Function} throttled function + */ + +function throttle(fn, interval) { + var throttling = false; + return function() { + if (throttling) { + return; + } + + fn.apply(void 0, arguments); + throttling = true; + setTimeout(function() { + throttling = false; + }, interval); + }; +} +/** + * Bind function against target <this>. + * + * @param {Function} fn + * @param {Object} target + * + * @return {Function} bound function + */ + +function bind(fn, target) { + return fn.bind(target); +} + +function _typeof(obj) { + '@babel/helpers - typeof'; + + if (typeof Symbol === 'function' && typeof Symbol.iterator === 'symbol') { + _typeof = function(obj) { + return typeof obj; + }; + } else { + _typeof = function(obj) { + return obj && typeof Symbol === 'function' && obj.constructor === Symbol && obj !== Symbol.prototype ? 'symbol' : typeof obj; + }; + } + + return _typeof(obj); +} + +function _extends() { + _extends = Object.assign || function(target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i]; + + for (var key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key]; + } + } + } + + return target; + }; + + return _extends.apply(this, arguments); +} + +/** + * Convenience wrapper for `Object.assign`. + * + * @param {Object} target + * @param {...Object} others + * + * @return {Object} the target + */ + +function assign(target) { + for (var _len = arguments.length, others = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + others[_key - 1] = arguments[_key]; + } + + return _extends.apply(void 0, [target].concat(others)); +} +/** + * Sets a nested property of a given object to the specified value. + * + * This mutates the object and returns it. + * + * @param {Object} target The target of the set operation. + * @param {(string|number)[]} path The path to the nested value. + * @param {any} value The value to set. + */ + +function set(target, path, value) { + var currentTarget = target; + forEach(path, function(key, idx) { + if (typeof key !== 'number' && typeof key !== 'string') { + throw new Error('illegal key type: ' + _typeof(key) + '. Key should be of type number or string.'); + } + + if (key === 'constructor') { + throw new Error('illegal key: constructor'); + } + + if (key === '__proto__') { + throw new Error('illegal key: __proto__'); + } + + var nextKey = path[idx + 1]; + var nextTarget = currentTarget[key]; + + if (isDefined(nextKey) && isNil(nextTarget)) { + nextTarget = currentTarget[key] = isNaN(+nextKey) ? {} : []; + } + + if (isUndefined(nextKey)) { + if (isUndefined(value)) { + delete currentTarget[key]; + } else { + currentTarget[key] = value; + } + } else { + currentTarget = nextTarget; + } + }); + return target; +} +/** + * Gets a nested property of a given object. + * + * @param {Object} target The target of the get operation. + * @param {(string|number)[]} path The path to the nested value. + * @param {any} [defaultValue] The value to return if no value exists. + */ + +function get(target, path, defaultValue) { + var currentTarget = target; + forEach(path, function(key) { + // accessing nil property yields <undefined> + if (isNil(currentTarget)) { + currentTarget = undefined; + return false; + } + + currentTarget = currentTarget[key]; + }); + return isUndefined(currentTarget) ? defaultValue : currentTarget; +} +/** + * Pick given properties from the target object. + * + * @param {Object} target + * @param {Array} properties + * + * @return {Object} target + */ + +function pick(target, properties) { + var result = {}; + var obj = Object(target); + forEach(properties, function(prop) { + if (prop in obj) { + result[prop] = target[prop]; + } + }); + return result; +} +/** + * Pick all target properties, excluding the given ones. + * + * @param {Object} target + * @param {Array} properties + * + * @return {Object} target + */ + +function omit(target, properties) { + var result = {}; + var obj = Object(target); + forEach(obj, function(prop, key) { + if (properties.indexOf(key) === -1) { + result[key] = prop; + } + }); + return result; +} +/** + * Recursively merge `...sources` into given target. + * + * Does support merging objects; does not support merging arrays. + * + * @param {Object} target + * @param {...Object} sources + * + * @return {Object} the target + */ + +function merge(target) { + for (var _len2 = arguments.length, sources = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { + sources[_key2 - 1] = arguments[_key2]; + } + + if (!sources.length) { + return target; + } + + forEach(sources, function(source) { + // skip non-obj sources, i.e. null + if (!source || !isObject(source)) { + return; + } + + forEach(source, function(sourceVal, key) { + if (key === '__proto__') { + return; + } + + var targetVal = target[key]; + + if (isObject(sourceVal)) { + if (!isObject(targetVal)) { + // override target[key] with object + targetVal = {}; + } + + target[key] = merge(targetVal, sourceVal); + } else { + target[key] = sourceVal; + } + }); + }); + return target; +} + +export { assign, bind, debounce, ensureArray, every, filter, find, findIndex, flatten, forEach, get, groupBy, has, isArray, isDefined, isFunction, isNil, isNumber, isObject, isString, isUndefined, keys, map, matchPattern, merge, omit, pick, reduce, set, size, some, sortBy, throttle, unionBy, uniqueBy, values, without }; diff --git a/ruoyi-ui/apps/web-antd/src/views/workflow/category/index.vue b/ruoyi-ui/apps/web-antd/src/views/workflow/category/index.vue index 31b29f4..ac75df5 100644 --- a/ruoyi-ui/apps/web-antd/src/views/workflow/category/index.vue +++ b/ruoyi-ui/apps/web-antd/src/views/workflow/category/index.vue @@ -1,156 +1,203 @@ -<script setup lang="ts"> -import type { VbenFormProps } from '@vben/common-ui'; -import type { Recordable } from '@vben/types'; +<template> + <div class="app-container"> + <div class="search" v-show="showSearch"> + <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> + <el-form-item label="鍒嗙被鍚嶇О" prop="categoryName"> + <el-input v-model="queryParams.categoryName" placeholder="璇疯緭鍏ュ垎绫诲悕绉�" clearable @keyup.enter="handleQuery" /> + </el-form-item> + <el-form-item label="鍒嗙被缂栫爜" prop="code"> + <el-input v-model="queryParams.code" placeholder="璇疯緭鍏ュ垎绫荤紪鐮�" clearable @keyup.enter="handleQuery" /> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button> + <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button> + </el-form-item> + </el-form> + </div> + <el-card shadow="never"> + <template #header> + <el-row :gutter="10" class="mb8"> + <el-col :span="1.5"> + <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['workflow:category:add']">鏂板</el-button> + </el-col> + <el-col :span="1.5"> + <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['workflow:category:edit']"> + 淇敼 + </el-button> + </el-col> + <el-col :span="1.5"> + <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['workflow:category:remove']"> + 鍒犻櫎 + </el-button> + </el-col> + <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> + </el-row> + </template> -import type { VxeGridProps } from '#/adapter/vxe-table'; + <el-table v-loading="loading" :data="categoryList" @selection-change="handleSelectionChange"> + <el-table-column type="selection" width="55" align="center" /> + <el-table-column label="鍒嗙被缂栧彿" align="center" prop="categoryId" v-if="true" /> + <el-table-column label="鍒嗙被鍚嶇О" align="center" prop="categoryName" /> + <el-table-column label="鍒嗙被缂栫爜" align="center" prop="code" /> + <el-table-column label="澶囨敞" align="center" prop="remark" /> + <el-table-column label="鎿嶄綔" align="center" width="150" class-name="small-padding fixed-width"> + <template #default="scope"> + <el-tooltip content="淇敼" placement="top"> + <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['workflow:category:edit']"></el-button> + </el-tooltip> + <el-tooltip content="鍒犻櫎" placement="top"> + <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['workflow:category:remove']"></el-button> + </el-tooltip> + </template> + </el-table-column> + </el-table> + <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> + </el-card> -import { nextTick } from 'vue'; + <!-- 娣诲姞鎴栦慨鏀瑰弬鏁伴厤缃璇濇 --> + <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body> + <el-form ref="categoryFormRef" :model="form" :rules="rules" label-width="80px"> + <el-form-item label="鍒嗙被鍚嶇О" prop="categoryName"> + <el-input v-model="form.categoryName" placeholder="璇疯緭鍏ュ垎绫诲悕绉�" /> + </el-form-item> + <el-form-item label="鍒嗙被缂栫爜" prop="code"> + <el-input v-model="form.code" placeholder="璇疯緭鍏ュ垎绫荤紪鐮�" /> + </el-form-item> + <el-form-item label="澶囨敞" prop="remark"> + <el-input v-model="form.remark" type="textarea" placeholder="璇疯緭鍏ュ唴瀹�" /> + </el-form-item> + </el-form> + <template #footer> + <div class="dialog-footer"> + <el-button type="primary" @click="submitForm">纭� 瀹�</el-button> + <el-button @click="cancel">鍙� 娑�</el-button> + </div> + </template> + </el-dialog> + </div> +</template> -import { Page, useVbenModal } from '@vben/common-ui'; -import { getVxePopupContainer } from '@vben/utils'; +<script setup name="Category" > +import { listCategory, getCategory, delCategory, addCategory, updateCategory } from "#/api/workflow/category"; -import { Popconfirm, Space } from 'ant-design-vue'; +const { proxy } = getCurrentInstance(); -import { useVbenVxeGrid } from '#/adapter/vxe-table'; -import { categoryList, categoryRemove } from '#/api/workflow/category'; +const categoryList = ref([]); +const loading = ref(true); +const showSearch = ref(true); +const ids = ref([]); +const single = ref(true); +const multiple = ref(true); +const total = ref(0); -import categoryModal from './category-modal.vue'; -import { columns, querySchema } from './data'; - -const formOptions: VbenFormProps = { - commonConfig: { - labelWidth: 80, - componentProps: { - allowClear: true, - }, +const queryFormRef = ref(); +const categoryFormRef = ref(); +const dialog = reactive({ + visible: false, + title: '' +}); +const initFormData = { + categoryId: undefined, + categoryName: '', + code: '', + remark: '' +} +const data = reactive({ + form: {...initFormData}, + queryParams: { + pageNum: 1, + pageSize: 10, + categoryName: '', + code: '' }, - schema: querySchema(), - wrapperClass: 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4', -}; - -const gridOptions: VxeGridProps = { - columns, - height: 'auto', - keepSource: true, - pagerConfig: { - enabled: false, - }, - proxyConfig: { - ajax: { - query: async (_, formValues = {}) => { - const resp = await categoryList({ - ...formValues, - }); - return { rows: resp }; - }, - // 榛樿璇锋眰鎺ュ彛鍚庡睍寮�鍏ㄩ儴 涓嶉渶瑕佸彲浠ュ垹闄よ繖娈� - querySuccess: () => { - nextTick(() => { - expandAll(); - }); - }, - }, - }, - /** - * 铏氭嫙婊氬姩 榛樿鍏抽棴 - */ - scrollY: { - enabled: false, - gt: 0, - }, - rowConfig: { - keyField: 'categoryId', - }, - treeConfig: { - parentField: 'parentId', - rowField: 'categoryId', - transform: true, - }, - // 琛ㄦ牸鍏ㄥ眬鍞竴琛ㄧず 淇濆瓨鍒楅厤缃渶瑕佺敤鍒� - id: 'workflow-category-index', -}; - -const [BasicTable, tableApi] = useVbenVxeGrid({ formOptions, gridOptions }); -const [CategoryModal, modalApi] = useVbenModal({ - connectedComponent: categoryModal, + rules: { + categoryName: [{ required: true, message: "鍒嗙被鍚嶇О涓嶈兘涓虹┖", trigger: "blur" }], + code: [{ required: true, message: "鍒嗙被缂栫爜涓嶈兘涓虹┖", trigger: "blur" }] + } }); -function handleAdd(row?: Recordable<any>) { - modalApi.setData({ parentId: row?.categoryId }); - modalApi.open(); +const { queryParams, form, rules } = toRefs(data); + +/** 鏌ヨ鍙傛暟鍒楄〃 */ +const getList = async () => { + loading.value = true; + const res = await listCategory(queryParams.value); + categoryList.value = res.rows; + total.value = res.total; + loading.value = false; +} +/** 鍙栨秷鎸夐挳 */ +const cancel = () => { + reset(); + dialog.visible = false; +} +/** 琛ㄥ崟閲嶇疆 */ +const reset = () => { + form.value = {...initFormData}; + categoryFormRef.value.resetFields(); +} +/** 鎼滅储鎸夐挳鎿嶄綔 */ +const handleQuery = () => { + queryParams.value.pageNum = 1; + getList(); +} +/** 閲嶇疆鎸夐挳鎿嶄綔 */ +const resetQuery = () => { + queryFormRef.value.resetFields(); + handleQuery(); +} +/** 澶氶�夋閫変腑鏁版嵁 */ +const handleSelectionChange = (selection) => { + ids.value = selection.map(item => item.categoryId); + single.value = selection.length != 1; + multiple.value = !selection.length; +} +/** 鏂板鎸夐挳鎿嶄綔 */ +const handleAdd = () => { + dialog.visible = true; + dialog.title = "娣诲姞鍙傛暟"; + nextTick(() => { + reset(); + }) +} +/** 淇敼鎸夐挳鎿嶄綔 */ +const handleUpdate = (row) => { + dialog.visible = true; + dialog.title = "淇敼鍙傛暟"; + const categoryId = row?.categoryId || ids.value[0]; + nextTick(async () => { + reset(); + const res = await getCategory(categoryId); + form.value = res.data; + }) +} +/** 鎻愪氦鎸夐挳 */ +const submitForm = () => { + categoryFormRef.value.validate(async (valid) => { + if (valid) { + form.value.categoryId ? await updateCategory(form.value) : await addCategory(form.value); + proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛"); + dialog.visible = false; + getList(); + } + }); +} +/** 鍒犻櫎鎸夐挳鎿嶄綔 */ +const handleDelete = async (row) => { + const categoryIds = row?.categoryId || ids.value; + await proxy?.$modal.confirm('鏄惁纭鍒犻櫎鍙傛暟缂栧彿涓�"' + categoryIds + '"鐨勬暟鎹」锛�'); + await delCategory(categoryIds); + getList(); + proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛"); +} +/** 瀵煎嚭鎸夐挳鎿嶄綔 */ +const handleExport = () => { + proxy?.download("workflow/category/export", { + ...queryParams.value + }, `category_${new Date().getTime()}.xlsx`); } -async function handleEdit(row: Recordable<any>) { - modalApi.setData({ id: row.categoryId }); - modalApi.open(); -} - -async function handleDelete(row: Recordable<any>) { - await categoryRemove(row.categoryId); - await tableApi.query(); -} - -function expandAll() { - tableApi.grid?.setAllTreeExpand(true); -} - -function collapseAll() { - tableApi.grid?.setAllTreeExpand(false); -} +onMounted(() => { + getList(); +}) </script> - -<template> - <Page :auto-content-height="true"> - <BasicTable table-title="娴佺▼鍒嗙被鍒楄〃"> - <template #toolbar-tools> - <Space> - <a-button @click="collapseAll"> - {{ $t('pages.common.collapse') }} - </a-button> - <a-button @click="expandAll"> - {{ $t('pages.common.expand') }} - </a-button> - <a-button - type="primary" - v-access:code="['workflow:category:add']" - @click="handleAdd" - > - {{ $t('pages.common.add') }} - </a-button> - </Space> - </template> - <template #action="{ row }"> - <Space> - <ghost-button - v-access:code="['workflow:category:edit']" - @click.stop="handleEdit(row)" - > - {{ $t('pages.common.edit') }} - </ghost-button> - <ghost-button - class="btn-success" - v-access:code="['workflow:category:edit']" - @click.stop="handleAdd(row)" - > - {{ $t('pages.common.add') }} - </ghost-button> - <Popconfirm - :get-popup-container="getVxePopupContainer" - placement="left" - title="纭鍒犻櫎锛�" - @confirm="handleDelete(row)" - > - <ghost-button - danger - v-access:code="['workflow:category:remove']" - @click.stop="" - > - {{ $t('pages.common.delete') }} - </ghost-button> - </Popconfirm> - </Space> - </template> - </BasicTable> - <CategoryModal @reload="tableApi.query()" /> - </Page> -</template> diff --git a/ruoyi-ui/apps/web-antd/src/views/workflow/deploy/index.vue b/ruoyi-ui/apps/web-antd/src/views/workflow/deploy/index.vue new file mode 100644 index 0000000..0ff3fe1 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/views/workflow/deploy/index.vue @@ -0,0 +1,236 @@ +<template> + <div class="app-container"> + <div class="search" v-show="showSearch"> + <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px"> + <el-form-item label="娴佺▼鏍囪瘑" prop="processKey"> + <el-input v-model="queryParams.processKey" placeholder="璇疯緭鍏ユ祦绋嬫爣璇�" clearable @keyup.enter="handleQuery" /> + </el-form-item> + <el-form-item label="娴佺▼鍚嶇О" prop="processName"> + <el-input v-model="queryParams.processName" placeholder="璇疯緭鍏ユ祦绋嬪悕绉�" clearable @keyup.enter="handleQuery" /> + </el-form-item> + <el-form-item label="娴佺▼鍒嗙被" prop="category"> + <el-select v-model="queryParams.category" clearable placeholder="娴佺▼鍒嗙被" style="width: 240px"> + <el-option v-for="item in categoryOptions" :key="item.categoryId" :label="item.categoryName" :value="item.code" /> + </el-select> + </el-form-item> + <el-form-item label="鐘舵��" prop="state"> + <el-select v-model="queryParams.state" clearable placeholder="璇烽�夋嫨鐘舵��" style="width: 240px"> + <el-option :key="1" label="婵�娲�" value="active" /> + <el-option :key="2" label="鎸傝捣" value="suspended" /> + </el-select> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button> + <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button> + </el-form-item> + </el-form> + </div> + <el-card shadow="never"> + <template #header> + <el-row :gutter="10" class="mb8"> + <el-col :span="1.5"> + <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['workflow:deploy:remove']"> + 鍒犻櫎 + </el-button> + </el-col> + <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> + </el-row> + </template> + + <el-table v-loading="loading" fit :data="deployList" @selection-change="handleSelectionChange"> + <el-table-column type="selection" width="55" align="center" /> + <el-table-column label="娴佺▼鏍囪瘑" align="center" prop="processKey" :show-overflow-tooltip="true" /> + <el-table-column label="娴佺▼鍚嶇О" align="center" prop="processName" :show-overflow-tooltip="true" /> + <el-table-column label="娴佺▼鍒嗙被" align="center" prop="categoryName" :formatter="categoryFormat" /> + <el-table-column label="娴佺▼鐗堟湰" align="center"> + <template #default="scope"> + <el-tag size="small">v{{ scope.row.version }}</el-tag> + </template> + </el-table-column> + <el-table-column label="鐘舵��" align="center"> + <template #default="scope"> + <el-tag type="success" v-if="!scope.row.suspended">婵�娲�</el-tag> + <el-tag type="warning" v-if="scope.row.suspended">鎸傝捣</el-tag> + </template> + </el-table-column> + <el-table-column label="閮ㄧ讲鏃堕棿" align="center" prop="deploymentTime" width="180" > + <template #default="scope"> + {{ parseTime(scope.row.deploymentTime, '{y}-{m}-{d} {h}:{i}:{s}')}} + </template> + </el-table-column> + <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width"> + <template #default="scope"> + <el-tooltip content="鐗堟湰绠$悊" placement="top"> + <el-button link type="primary" icon="PriceTag" @click="handlePublish(scope.row)" v-hasPermi="['workflow:deploy:list']"></el-button> + </el-tooltip> + <el-tooltip content="鍒犻櫎" placement="top"> + <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['workflow:deploy:remove']"></el-button> + </el-tooltip> + </template> + </el-table-column> + </el-table> + <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> + </el-card> + +<!-- <!– 娴佺▼鍥惧璇濇 –>--> +<!-- <el-dialog :title="processDialog.title" v-model="processDialog.visible" width="500px" append-to-body>--> +<!-- <process-viewer :key="`designer-${processView.index}`" :xml="processView.xmlData" :style="{height: '400px'}" />--> +<!-- </el-dialog>--> + + <!-- 鐗堟湰绠$悊 --> + <el-dialog title="鐗堟湰绠$悊" v-model="publishDialog.visible" width="700px" append-to-body> + <el-table v-loading="publishLoading" :data="publishList"> + <el-table-column label="娴佺▼鏍囪瘑" align="center" prop="processKey" :show-overflow-tooltip="true" /> + <el-table-column label="娴佺▼鍚嶇О" align="center" :show-overflow-tooltip="true"> + <template #default="scope"> + <a class="link-type" @click="handleProcessView(scope.row)"> + <span>{{ scope.row.processName }}</span> + </a> + </template> + </el-table-column> + <el-table-column label="娴佺▼鐗堟湰" align="center"> + <template #default="scope"> + <el-tag size="small">v{{ scope.row.version }}</el-tag> + </template> + </el-table-column> + <el-table-column label="鐘舵��" align="center"> + <template #default="scope"> + <el-tag type="success" v-if="!scope.row.suspended">婵�娲�</el-tag> + <el-tag type="warning" v-if="scope.row.suspended">鎸傝捣</el-tag> + </template> + </el-table-column> + <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width"> + <template #default="scope"> + <el-tooltip content="鎸傝捣" placement="top"> + <el-button link type="primary" icon="VideoPause" @click="handleChangeState(scope.row, 'suspended')" v-hasPermi="['workflow:deploy:status']"></el-button> + </el-tooltip> + <el-tooltip content="婵�娲�" placement="top"> + <el-button link type="primary" icon="VideoPlay" @click="handleChangeState(scope.row, 'active')" v-hasPermi="['workflow:deploy:status']"></el-button> + </el-tooltip> + <el-tooltip content="鍒犻櫎" placement="top"> + <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['workflow:deploy:remove']"></el-button> + </el-tooltip> + </template> + </el-table-column> + </el-table> + <pagination v-show="publishTotal > 0" :total="publishTotal" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getPublishList" /> + </el-dialog> + </div> +</template> + +<script setup name="Deploy" lang="js"> +import { listAllCategory } from '#/api/workflow/category' +import { listDeploy, listPublish, getBpmnXml, changeState, delDeploy } from '#/api/workflow/deploy' +import { ElForm } from 'element-plus'; + +const { proxy } = getCurrentInstance() ; + +const categoryOptions = ref([]); + +const deployList = ref([]); +const loading = ref(true); +const showSearch = ref(true) +const ids = ref([]); +const single = ref(true); +const multiple = ref(true); +const total = ref(0); +const publishList = ref([]); +const publishLoading = ref(true); +const publishTotal = ref(0); + +const queryFormRef = ref(); +const processDialog = reactive({ + visible: false, + title: '' +}); +const publishDialog = reactive({ + visible: false, + title: '' +}); +const data = reactive({ + form: {}, + queryParams: { + pageNum: 1, + pageSize: 10, + processKey: '', + processName: '', + category: '' + }, + rules: {} +}); +const { queryParams } = toRefs(data); + +/** 鏌ヨ娴佺▼鍒嗙被鍒楄〃 */ +const getCategoryList = async () => { + const res = await listAllCategory(); + categoryOptions.value = res.data; +}; + +const getList = async () => { + loading.value = true; + const res = await listDeploy(queryParams.value); + deployList.value = res.rows; + total.value = res.total; + loading.value = false; +} +/** 鎼滅储鎸夐挳鎿嶄綔 */ +const handleQuery = () => { + queryParams.value.pageNum = 1; + getList(); +} +/** 閲嶇疆鎸夐挳鎿嶄綔 */ +const resetQuery = () => { + queryFormRef.value.resetFields(); + handleQuery(); +} +/** 澶氶�夋閫変腑鏁版嵁 */ +const handleSelectionChange = (selection) => { + ids.value = selection.map(item => item.deploymentId); + single.value = selection.length != 1; + multiple.value = !selection.length; +} +/** 鍒犻櫎鎸夐挳鎿嶄綔 */ +const handleDelete = async (row) => { + const deploymentIds = row.deploymentId || ids.value; + await proxy.$modal.confirm('鏄惁纭鍒犻櫎鍙傛暟缂栧彿涓�"' + deploymentIds + '"鐨勬暟鎹」锛�'); + await delDeploy(deploymentIds); + getList(); + proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛"); +} +const getPublishList = async () => { + publishLoading.value = true; + const res = await listPublish(queryParams.value); + publishList.value = res.rows; + publishTotal.value = res.total; + publishLoading.value = false; +} +const handlePublish = (row) => { + queryParams.value.processKey = row.processKey; + publishDialog.visible = true; + getPublishList(); +} +const handleProcessView = (row) => { + +} +const handleChangeState = async (row, state) => { + const params = { + definitionId: row.definitionId, + state: state + } + await changeState(params); + proxy.$modal.msgSuccess("鎿嶄綔鎴愬姛"); + getPublishList(); +} + +const categoryFormat = (row) => { + var category = categoryOptions.value.find(function(k) { + return k.code === row.category; + }); + return category ? category.categoryName : ''; +} + +onMounted(() => { + getCategoryList(); + getList(); +}) +</script> diff --git a/ruoyi-ui/apps/web-antd/src/views/workflow/form/index.vue b/ruoyi-ui/apps/web-antd/src/views/workflow/form/index.vue new file mode 100644 index 0000000..75aeab5 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/views/workflow/form/index.vue @@ -0,0 +1,251 @@ +<template> + <div class="app-container"> + <div class="search" v-show="showSearch"> + <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="70"> + <el-form-item label="琛ㄥ崟鍚嶇О" prop="formName"> + <el-input v-model="queryParams.formName" placeholder="璇疯緭鍏ヨ〃鍗曞悕绉�" clearable @keyup.enter="handleQuery" /> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button> + <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button> + </el-form-item> + </el-form> + </div> + <el-card shadow="never"> + <template #header> + <el-row :gutter="10" class="mb8"> + <el-col :span="1.5"> + <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['workflow:form:add']">鏂板</el-button> + </el-col> + <el-col :span="1.5"> + <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['workflow:form:edit']">淇敼</el-button> + </el-col> + <el-col :span="1.5"> + <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['workflow:form:remove']">鍒犻櫎</el-button> + </el-col> + <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> + </el-row> + </template> + + <el-table v-loading="loading" :data="formList" @selection-change="handleSelectionChange"> + <el-table-column type="selection" width="55" align="center" /> + <el-table-column label="琛ㄥ崟涓婚敭" align="center" prop="formId" v-if="false" /> + <el-table-column label="琛ㄥ崟鍚嶇О" align="center" prop="formName" /> + <el-table-column label="澶囨敞" align="center" prop="remark" /> + <el-table-column label="鎿嶄綔" align="center" class-name="small-padding fixed-width"> + <template #default="scope"> + <el-tooltip content="璇︽儏" placement="top"> + <el-button link type="primary" icon="View" @click="handleDetail(scope.row)" v-hasPermi="['workflow:form:query']"></el-button> + </el-tooltip> + <el-tooltip content="淇敼" placement="top"> + <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['workflow:form:edit']"></el-button> + </el-tooltip> + <el-tooltip content="鍒犻櫎" placement="top"> + <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['workflow:form:remove']"></el-button> + </el-tooltip> + </template> + </el-table-column> + </el-table> + <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> + </el-card> + + <!-- 娴佺▼琛ㄥ崟璁捐鍣ㄥ璇濇 --> + <el-dialog :title="designer.title" v-model="designer.visible" fullscreen> + <div id="form-designer"> + <v-form-designer ref="vfDesignerRef" :resetFormJson="true" :designer-config="designerConfig"> + <!-- 鑷畾涔夋寜閽彃妲� --> + <template #customToolButtons> + <el-button link type="primary" icon="Finished" @click="dialog.visible = true">淇濆瓨</el-button> + </template> + </v-form-designer> + </div> + </el-dialog> + + <!-- 棰勮琛ㄥ崟瀵硅瘽妗� --> + <el-dialog :title="render.title" v-model="render.visible" width="60%" append-to-body> + <v-form-render :form-json="{}" :form-data="{}" ref="vfRenderRef" /> + </el-dialog> + + <!-- 娣诲姞鎴栦慨鏀规祦绋嬭〃鍗曞璇濇 --> + <el-dialog :title="dialog.title" v-model="dialog.visible" width="600px" append-to-body> + <el-form ref="formFormRef" :model="form" :rules="rules" label-width="80px"> + <el-form-item label="琛ㄥ崟鍚嶇О" prop="formName"> + <el-input v-model="form.formName" placeholder="璇疯緭鍏ヨ〃鍗曞悕绉�" /> + </el-form-item> + <el-form-item label="澶囨敞" prop="remark"> + <el-input v-model="form.remark" placeholder="璇疯緭鍏ュ娉�" /> + </el-form-item> + </el-form> + <template #footer> + <div class="dialog-footer"> + <el-button type="primary" @click="submitForm">纭� 瀹�</el-button> + <el-button @click="cancel">鍙� 娑�</el-button> + </div> + </template> + </el-dialog> + </div> +</template> + +<script setup name="Form" > +import { listForm, addForm, updateForm, getForm, delForm } from "#/api/workflow/form"; + +const { proxy } = getCurrentInstance(); + +const formList = ref([]); +const loading = ref(true); +const showSearch = ref(true); +const ids = ref([]); +const single = ref(true); +const multiple = ref(true); +const total = ref(0); + +const vfDesignerRef = ref(null); +const vfRenderRef = ref(null); +const formFormRef = ref(); +const queryFormRef = ref(); + +const designerConfig = reactive({ + externalLink: true, + toolbarMaxWidth: 510, + // languageMenu: true, + //externalLink: false, + //formTemplates: false, + //eventCollapse: false, + //clearDesignerButton: false, + //previewFormButton: false, +}) +const designer = reactive({ + visible: false, + title: '' +}) +const render = reactive({ + visible: false, + title: '' +}) +const dialog = reactive({ + visible: false, + title: '' +}); +const initFormData = { + formId: undefined, + formName: '', + content: '', + remark: '' +} +const data = reactive({ + form: {...initFormData}, + queryParams: { + pageNum: 1, + pageSize: 10, + formName: '' + }, + rules: { + formName: [{ required: true, message: "琛ㄥ崟鍚嶇О涓嶈兘涓虹┖", trigger: "blur" }] + } +}); + +const { queryParams, form, rules } = toRefs(data); + +/** 鏌ヨ宀椾綅鍒楄〃 */ +const getList = async () => { + loading.value = true; + const res = await listForm(queryParams.value); + formList.value = res.rows; + total.value = res.total; + loading.value = false; +} +/** 鍙栨秷鎸夐挳 */ +const cancel = () => { + reset(); + dialog.visible = false; +} +/** 琛ㄥ崟閲嶇疆 */ +const reset = () => { + form.value = {...initFormData}; +} +/** 鎼滅储鎸夐挳鎿嶄綔 */ +const handleQuery = () => { + queryParams.value.pageNum = 1; + getList(); +} +/** 閲嶇疆鎸夐挳鎿嶄綔 */ +const resetQuery = () => { + queryFormRef.value.resetFields(); + handleQuery(); +} +/** 澶氶�夋閫変腑鏁版嵁 */ +const handleSelectionChange = (selection) => { + ids.value = selection.map(item => item.formId); + single.value = selection.length != 1; + multiple.value = !selection.length; +} +/** 鏂板琛ㄥ崟鎿嶄綔 */ +const handleAdd = () => { + designer.visible = true; + nextTick(() => { + reset(); + vfDesignerRef.value.clearDesigner(); + }) +} +/** 淇敼琛ㄥ崟鎿嶄綔 */ +const handleUpdate = (row) => { + designer.visible = true; + nextTick(async () => { + const formId = row?.formId || ids.value[0]; + const res = await getForm(formId); + form.value = res.data; + vfDesignerRef.value.setFormJson(form.value.content); + }) +} +/** 鏌ョ湅琛ㄥ崟鎿嶄綔 */ +const handleDetail = (row) => { + render.visible = true; + render.title = '鏌ョ湅琛ㄥ崟璇︽儏'; + nextTick(async () => { + vfRenderRef.value.setFormJson(row.content || {formConfig: {}, widgetList: []}); + }); +} +/** 鎻愪氦琛ㄥ崟鎿嶄綔 */ +const submitForm = () => { + const formJson = vfDesignerRef.value.getFormJson(); + form.value.content = JSON.stringify(formJson); + nextTick(async () => { + form.value.formId ? await updateForm(form.value) : await addForm(form.value); + proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛"); + dialog.visible = false; + designer.visible = false; + getList(); + }) +} +/** 鍒犻櫎鎸夐挳鎿嶄綔 */ +const handleDelete = async (row) => { + const formIds = row?.formId || ids.value; + await proxy?.$modal.confirm('鏄惁纭鍒犻櫎琛ㄥ崟缂栧彿涓�"' + formIds + '"鐨勬暟鎹」锛�'); + await delForm(formIds); + getList(); + proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛"); +} +/** 瀵煎嚭鎸夐挳鎿嶄綔 */ +const handleExport = () => { + proxy?.download("workflow/form/export", { + ...queryParams.value + }, `form_${new Date().getTime()}.xlsx`); +} + +getList(); +</script> + +<style lang="scss" scoped> +#form-designer { + .main-container { + margin: 0; + } + label { + font-weight: normal; + } + :deep(.external-link) { + display: flex; + align-items: center; + } +} +</style> diff --git a/ruoyi-ui/apps/web-antd/src/views/workflow/listener/index.vue b/ruoyi-ui/apps/web-antd/src/views/workflow/listener/index.vue new file mode 100644 index 0000000..5a89532 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/views/workflow/listener/index.vue @@ -0,0 +1,469 @@ +<template> + <div class="app-container"> + <!-- 鏌ヨ鏉′欢 --> + <div v-show="showSearch" class="search"> + <el-form ref="queryFormRef" :inline="true" :model="queryParams" label-width="120"> + <el-form-item label="鐩戝惉绫诲瀷" prop="listenerType"> + <el-select v-model="queryParams.listenerType" clearable placeholder="璇烽�夋嫨鐩戝惉绫诲瀷" @change="handleQuery"> + <el-option v-for="dict in sys_listener_type" :key="dict.value" :label="dict.label" :value="dict.value" /> + </el-select> + </el-form-item> + <el-form-item label="鍚嶇О" prop="name"> + <el-input v-model="queryParams.name" clearable placeholder="璇疯緭鍏ュ悕绉�" @keyup.enter="handleQuery" /> + </el-form-item> + <el-form-item label="浜嬩欢绫诲瀷" prop="eventType"> + <el-select v-model="queryParams.eventType" clearable placeholder="璇烽�夋嫨浜嬩欢绫诲瀷" @change="handleQuery"> + <el-option + v-for="dict in sys_task_listener_event_type" + v-if="queryParams.listenerType === 'task'" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + <el-option + v-for="dict in sys_execution_listener_event_type" + v-if="queryParams.listenerType === 'execution'" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + <el-option + v-for="dict in sys_task_listener_event_type" + v-if="!queryParams.listenerType" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + <el-option + v-for="dict in sys_execution_listener_event_type" + v-if="!queryParams.listenerType" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item> + <el-button icon="Search" type="primary" @click="handleQuery">鎼滅储</el-button> + <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button> + </el-form-item> + </el-form> + </div> + + <el-card shadow="never"> + <template #header> + <!-- 鎿嶄綔鎸夐挳 --> + <el-row :gutter="10" class="mb8"> + <el-col :span="1.5"> + <el-button v-hasPermi="['workflow:listener:add']" icon="Plus" plain type="primary" @click="handleAdd">鏂板</el-button> + </el-col> + <el-col :span="1.5"> + <el-button v-hasPermi="['workflow:listener:edit']" :disabled="single" icon="Edit" plain type="success" @click="handleUpdate()" + >淇敼</el-button + > + </el-col> + <el-col :span="1.5"> + <el-button v-hasPermi="['workflow:listener:remove']" :disabled="multiple" icon="Delete" plain type="danger" @click="handleDelete()" + >鍒犻櫎</el-button + > + </el-col> + <el-col :span="1.5"> + <el-button v-hasPermi="['workflow:listener:export']" icon="Download" plain type="warning" @click="handleExport">瀵煎嚭</el-button> + </el-col> + <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> + </el-row> + </template> + + <!-- 鍒嗛〉鍒楄〃 --> + <el-table + v-loading="tableLoading" + :data="listenerList" + @selection-change="handleSelectionChange" + v-fixTableHeight + highlight-current-row + show-overflow-tooltip + > + <el-table-column align="center" type="selection" width="55" /> + <el-table-column type="expand" width="50px"> + <template #default="props"> + <el-table :data="props.row.fields" width="60%"> + <el-table-column v-if="false" label="涓婚敭" prop="id" /> + <el-table-column label="瀛楁鍚嶇О" prop="fieldName" /> + <el-table-column label="瀛楁绫诲瀷" prop="fieldType"> + <template #default="scope"> + <dict-tag :options="sys_listener_field_type" :value="scope.row.fieldType" /> + </template> + </el-table-column> + <el-table-column label="瀛楁鍊�" prop="fieldValue" /> + <el-table-column align="center" class-name="small-padding fixed-width" label="鎿嶄綔"> + <template #default="scope"> + <el-tooltip content="淇敼" placement="top"> + <el-button + v-hasPermi="['workflow:listener:edit']" + icon="Edit" + link + type="primary" + @click="handleFieldUpdate(scope.row)" + ></el-button> + </el-tooltip> + <el-tooltip content="鍒犻櫎" placement="top"> + <el-button + v-hasPermi="['workflow:listener:remove']" + icon="Delete" + link + type="danger" + @click="handleFieldDelete(scope.row)" + ></el-button> + </el-tooltip> + </template> + </el-table-column> + </el-table> + </template> + </el-table-column> + <el-table-column v-if="false" align="center" label="涓婚敭" prop="id" /> + <el-table-column align="center" label="鐩戝惉绫诲瀷" prop="listenerType"> + <template #default="scope"> + <dict-tag :options="sys_listener_type" :value="scope.row.listenerType" /> + </template> + </el-table-column> + <el-table-column align="center" label="鍚嶇О" prop="name" /> + <el-table-column align="center" label="鍊肩被鍨�" prop="valueType"> + <template #default="scope"> + <dict-tag :options="sys_listener_value_type" :value="scope.row.valueType" /> + </template> + </el-table-column> + <el-table-column align="center" label="鍊�" prop="value" /> + <el-table-column align="center" label="浜嬩欢绫诲瀷" prop="eventType"> + <template #default="scope"> + <dict-tag v-if="scope.row.listenerType === 'task'" :options="sys_task_listener_event_type" :value="scope.row.eventType" /> + <dict-tag v-if="scope.row.listenerType === 'execution'" :options="sys_execution_listener_event_type" :value="scope.row.eventType" /> + </template> + </el-table-column> + <el-table-column align="center" class-name="small-padding fixed-width" label="鎿嶄綔"> + <template #default="scope"> + <el-tooltip content="娣诲姞瀛楁" placement="top"> + <el-button v-hasPermi="['workflow:listener:edit']" icon="Plus" link type="primary" @click="addField(scope.row)"></el-button> + </el-tooltip> + <el-tooltip content="淇敼" placement="top"> + <el-button v-hasPermi="['workflow:listener:edit']" icon="Edit" link type="primary" @click="handleUpdate(scope.row)"></el-button> + </el-tooltip> + <el-tooltip content="鍒犻櫎" placement="top"> + <el-button v-hasPermi="['workflow:listener:remove']" icon="Delete" link type="danger" @click="handleDelete(scope.row)"></el-button> + </el-tooltip> + </template> + </el-table-column> + </el-table> + <pagination v-show="total > 0" v-model:limit="queryParams.pageSize" v-model:page="queryParams.pageNum" :total="total" @pagination="getList" /> + </el-card> + + <!-- 娣诲姞鎴栦慨鏀规祦绋嬭〃鍗曞璇濇 --> + <el-dialog v-model="dialog.visible" :close-on-click-modal="false" :title="dialog.title" append-to-body width="600px"> + <el-form ref="listenerFormRef" v-loading="formLoading" :model="form" :rules="rules" label-width="120px"> + <el-form-item label="鐩戝惉绫诲瀷" prop="listenerType"> + <el-radio-group v-model="form.listenerType"> + <el-radio v-for="dict in sys_listener_type" :key="dict.value" :label="dict.value">{{ dict.label }}</el-radio> + </el-radio-group> + </el-form-item> + <el-form-item label="鍚嶇О" prop="name"> + <el-input v-model="form.name" placeholder="璇疯緭鍏ュ悕绉�" /> + </el-form-item> + <el-form-item label="鍊肩被鍨�" prop="valueType"> + <el-radio-group v-model="form.valueType"> + <el-radio v-for="dict in sys_listener_value_type" :key="dict.value" :label="dict.value">{{ dict.label }}</el-radio> + </el-radio-group> + </el-form-item> + <el-form-item label="鍊�" prop="value"> + <el-input v-model="form.value" placeholder="璇疯緭鍏ュ��" /> + </el-form-item> + <el-form-item label="浜嬩欢绫诲瀷" prop="eventType"> + <el-select v-model="form.eventType" clearable placeholder="璇烽�夋嫨浜嬩欢绫诲瀷" style="width: 100%"> + <el-option + v-for="dict in sys_task_listener_event_type" + v-if="form.listenerType === 'task'" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + <el-option + v-for="dict in sys_execution_listener_event_type" + v-if="form.listenerType === 'execution'" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + </el-form> + <template #footer> + <div class="dialog-footer"> + <el-button type="primary" @click="submitForm">纭� 瀹�</el-button> + <el-button @click="cancel">鍙� 娑�</el-button> + </div> + </template> + </el-dialog> + + <!-- 鐩戝惉鍣ㄥ瓧娈靛璇濇 --> + <el-dialog v-model="fieldDialog.visible" :close-on-click-modal="false" :title="fieldDialog.title" append-to-body width="600px"> + <el-form ref="listenerFieldFormRef" v-loading="fieldFormLoading" :model="initFieldFormData" :rules="filedRules" label-width="120px"> + <el-form-item label="鍚嶇О" prop="fieldName"> + <el-input v-model="filedForm.fieldName" placeholder="璇疯緭鍏ュ悕绉�" /> + </el-form-item> + <el-form-item label="瀛楁绫诲瀷" prop="fieldType"> + <el-radio-group v-model="filedForm.fieldType"> + <el-radio v-for="dict in sys_listener_field_type" :key="dict.value" :label="dict.value">{{ dict.label }}</el-radio> + </el-radio-group> + </el-form-item> + <el-form-item label="鍊�" prop="fieldValue"> + <el-input v-model="filedForm.fieldValue" placeholder="璇疯緭鍏ュ��" /> + </el-form-item> + </el-form> + <template #footer> + <div class="dialog-footer"> + <el-button type="primary" @click="submitFieldForm">纭� 瀹�</el-button> + <el-button @click="cancel">鍙� 娑�</el-button> + </div> + </template> + </el-dialog> + </div> +</template> + +<script name="Form" setup> +getList(); + +import { + addListener, + deleteListenerFieldAPI, + delListener, + getListener, + insertListenerFieldAPI, + queryListenerPage, + updateListener, + updateListenerFieldAPI +} from "#/api/workflow/listener"; + + + +const { proxy } = getCurrentInstance() ; +const { + sys_listener_type, + sys_listener_value_type, + sys_task_listener_event_type, + sys_execution_listener_event_type, + sys_listener_field_type +} = proxy.useDict("sys_listener_type", "sys_listener_value_type", "sys_task_listener_event_type", "sys_execution_listener_event_type", "sys_listener_field_type"); + +const listenerList = ref([]); +const showSearch = ref(true); +const ids = ref([]); +const names = ref([]); +const single = ref(true); +const multiple = ref(true); +const total = ref(0); +const tableLoading = ref(false); +const formLoading = ref(false); +const fieldFormLoading = ref(false); +const listenerFormRef = ref(); +const listenerFieldFormRef = ref(); +const queryFormRef = ref(); +const listenerId = ref(''); + +const dialog = reactive({ + visible: false, + title: '' +}); + +const fieldDialog = reactive({ + visible: false, + title: '' +}); +const initFormData = { + id: undefined, + valueType: '', + eventType: '', + listenerType: '', + name: '', + value: '' +} + +const data = reactive({ + form: {...initFormData}, + queryParams: { + pageNum: 1, + pageSize: 10, + name: '', + listenerType: '', + eventType: '' + }, + rules: { + listenerType: [{ required: true, message: "鐩戝惉绫诲瀷涓嶈兘涓虹┖", trigger: "blur" }], + name: [{ required: true, message: "鍚嶇О涓嶈兘涓虹┖", trigger: "blur" }], + valueType: [{ required: true, message: "鍊肩被鍨嬩笉鑳戒负绌�", trigger: "blur" }], + value: [{ required: true, message: "鍊间笉鑳戒负绌�", trigger: "blur" }] + } +}); + +const { queryParams, form, rules } = toRefs(data); + +const initFieldFormData={ + id: undefined, + listenerId: undefined, + fieldName: undefined, + fieldType: undefined, + fieldValue: undefined +} + +const filedData = reactive({ + filedForm: {...initFieldFormData}, + filedRules: { + fieldName: [{ required: true, message: "璇疯緭鍏ュ瓧娈靛悕绉�", trigger: "blur" }], + fieldType: [{ required: true, message: "璇烽�夋嫨瀛楁绫诲瀷", trigger: "blur" }] + } +}) + +const { filedForm, filedRules } = toRefs(filedData); + +/** 鏌ヨ鍒楄〃 */ +const getList = async () => { + tableLoading.value = true; + const res = await queryListenerPage(queryParams.value); + listenerList.value = res.rows; + total.value = res.total; + tableLoading.value = false; +} +/** 鍙栨秷鎸夐挳 */ +const cancel = () => { + reset(); + dialog.visible = false; + fieldDialog.visible = false; +} +/** 琛ㄥ崟閲嶇疆 */ +const reset = () => { + form.value = {...initFormData}; + filedForm.value = {...initFieldFormData}; +} +/** 鎼滅储鎸夐挳鎿嶄綔 */ +const handleQuery = () => { + queryParams.value.pageNum = 1; + getList(); +} +/** 閲嶇疆鎸夐挳鎿嶄綔 */ +const resetQuery = () => { + queryFormRef.value.resetFields(); + handleQuery(); +} +/** 澶氶�夋閫変腑鏁版嵁 */ +const handleSelectionChange = (selection) => { + ids.value = selection.map(item => item.id); + names.value = selection.map(item => item.name); + single.value = selection.length != 1; + multiple.value = !selection.length; +} +/** 鏂板琛ㄥ崟鎿嶄綔 */ +const handleAdd = () => { + dialog.visible = true; + nextTick(() => { + reset(); + }) +} +/** 淇敼琛ㄥ崟鎿嶄綔 */ +const handleUpdate = (row) => { + dialog.visible = true; + const id = row?.id || ids.value[0]; + nextTick(async () => { + formLoading.value = true; + reset(); + const res = await getListener(id); + formLoading.value = false; + form.value = res.data; + }) +} +/** 鎻愪氦琛ㄥ崟鎿嶄綔 */ +const submitForm = () => { + listenerFormRef.value.validate(async (valid) => { + if (valid) { + dialog.visible = true; + formLoading.value = true; + try { + form.value.id ? await updateListener(form.value) : await addListener(form.value); + } finally { + formLoading.value = false; + } + dialog.visible = false; + proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛"); + getList(); + } + }); +} +/** 鍒犻櫎鎸夐挳鎿嶄綔 */ +const handleDelete = async (row) => { + const listenerIds = row?.id || ids.value; + const listenerNames = row?.name || ids.value; + await proxy?.$modal.confirm('鏄惁纭鍒犻櫎銆�' + listenerNames + '銆戠殑鏁版嵁椤癸紵'); + tableLoading.value = true; + try { + await delListener(listenerIds); + } finally { + tableLoading.value = false; + } + getList(); + proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛"); +} +/** 娣诲姞瀛楁鎿嶄綔 */ +const addField = (ro) => { + fieldDialog.visible = true; + // listenerField.value = row.fields; + listenerId.value = row.id; + nextTick(() => { + reset(); + filedForm.value.listenerId = row.id; + }) +} +/** 淇敼瀛楁鎿嶄綔 */ +const handleFieldUpdate = (row) => { + fieldDialog.visible = true; + filedForm.value.id = row.id; + filedForm.value.listenerId = row.listenerId; + filedForm.value.fieldName = row.fieldName; + filedForm.value.fieldType = row.fieldType; + filedForm.value.fieldValue = row.fieldValue; +} +/** 鎻愪氦瀛楁琛ㄥ崟鎿嶄綔 */ +const submitFieldForm = () => { + listenerFieldFormRef.value.validate(async (vali) => { + if (valid) { + fieldDialog.visible = true; + fieldFormLoading.value = true; + try { + filedForm.value.id ? await updateListenerFieldAPI(filedForm.value) : await insertListenerFieldAPI(filedForm.value); + } finally { + fieldFormLoading.value = false; + } + listenerId.value = ''; + fieldDialog.visible = false; + proxy.$modal.msgSuccess("鎿嶄綔鎴愬姛"); + getList(); + } + }); +} +/** 鍒犻櫎瀛楁鎸夐挳鎿嶄綔 */ +const handleFieldDelete = async (ro) => { + const fieldIds = row?.id || ids.value; + const fieldNames = row?.fieldName || ids.value; + await proxy?.$modal.confirm('鏄惁纭鍒犻櫎銆�' + fieldNames + '銆戠殑鏁版嵁椤癸紵'); + tableLoading.value = true; + try { + await deleteListenerFieldAPI(fieldIds); + } finally { + tableLoading.value = false; + } + getList(); + proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛"); +} +/** 瀵煎嚭鎸夐挳鎿嶄綔 */ +const handleExport = () => { + proxy?.download("workflow/form/export", { + ...queryParams.value + }, `form_${new Date().getTime()}.xlsx`); +} + +</script> diff --git a/ruoyi-ui/apps/web-antd/src/views/workflow/model/index.vue b/ruoyi-ui/apps/web-antd/src/views/workflow/model/index.vue new file mode 100644 index 0000000..baa47df --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/views/workflow/model/index.vue @@ -0,0 +1,438 @@ +<template> + <div class="app-container"> + <div class="search" v-show="showSearch"> + <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="70"> + <el-form-item label="妯″瀷鏍囪瘑" prop="modelKey"> + <el-input v-model="queryParams.modelKey" placeholder="璇疯緭鍏ユā鍨嬫爣璇�" clearable style="width: 200px" @keyup.enter="handleQuery" /> + </el-form-item> + <el-form-item label="妯″瀷鍚嶇О" prop="modelName"> + <el-input v-model="queryParams.modelName" placeholder="璇疯緭鍏ユā鍨嬪悕绉�" clearable style="width: 200px" @keyup.enter="handleQuery" /> + </el-form-item> + <el-form-item label="娴佺▼鍒嗙被" prop="category"> + <el-select v-model="queryParams.category" clearable placeholder="璇烽�夋嫨" @change="handleQuery" style="width: 240px"> + <el-option + v-for="item in categoryOptions" + :key="item.categoryId" + :label="item.categoryName" + :value="item.code"> + </el-option> + </el-select> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button> + <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button> + </el-form-item> + </el-form> + </div> + <el-card shadow="never"> + <template #header> + <el-row :gutter="10" class="mb8"> + <el-col :span="1.5"> + <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['workflow:model:add']">鏂板</el-button> + </el-col> + <el-col :span="1.5"> + <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete" v-hasPermi="['workflow:model:remove']">鍒犻櫎</el-button> + </el-col> + <el-col :span="1.5"> + <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['workflow:model:export']">瀵煎嚭</el-button> + </el-col> + <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> + </el-row> + </template> + + <el-table v-loading="loading" :data="modelList" @selection-change="handleSelectionChange"> + <el-table-column type="selection" width="55" align="center" /> + <el-table-column label="妯″瀷鏍囪瘑" align="center" prop="modelKey" :show-overflow-tooltip="true" /> + <el-table-column label="妯″瀷鍚嶇О" align="center" :show-overflow-tooltip="true"> + <template #default="scope"> + <a class="link-type" @click="handleProcessView(scope.row)"> + <span>{{ scope.row.modelName }}</span> + </a> + </template> + </el-table-column> + <el-table-column label="娴佺▼鍒嗙被" align="center" prop="categoryName" :formatter="categoryFormat" /> + <el-table-column label="妯″瀷鐗堟湰" align="center"> + <template #default="scope"> + <el-tag size="small">v{{ scope.row.version }}</el-tag> + </template> + </el-table-column> + <el-table-column label="鎻忚堪" align="center" prop="description" :show-overflow-tooltip="true" /> + <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="180" > + <template #default="scope"> + {{ parseTime(scope.row.createTime, '{y}-{m}-{d} {h}:{i}:{s}')}} + </template> + </el-table-column> + <el-table-column label="鎿嶄綔" width="180" align="center" class-name="small-padding fixed-width"> + <template #default="scope"> + <el-tooltip content="淇敼" placement="top"> + <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['workflow:model:edit']"></el-button> + </el-tooltip> + <el-tooltip content="鍒犻櫎" placement="top"> + <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['workflow:model:remove']"></el-button> + </el-tooltip> + <el-tooltip content="璁捐" placement="top"> + <el-button link type="primary" icon="Brush" @click="handleDesigner(scope.row)" v-hasPermi="['workflow:model:designer']"></el-button> + </el-tooltip> + <el-tooltip content="閮ㄧ讲" placement="top"> + <el-button link type="primary" icon="Promotion" @click="handleDeploy(scope.row)" v-hasPermi="['workflow:model:deploy']"></el-button> + </el-tooltip> + <el-tooltip content="鍘嗗彶" placement="top"> + <el-button link type="primary" icon="Discount" @click="handleHistory(scope.row)" v-hasPermi="['workflow:model:list']"></el-button> + </el-tooltip> + </template> + </el-table-column> + </el-table> + + <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> + </el-card> + + <!-- 娣诲姞鎴栦慨鏀规ā鍨嬩俊鎭璇濇 --> + <el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body> + <el-form ref="modelFormRef" :model="form" :rules="rules" label-width="80px"> + <el-form-item label="妯″瀷鏍囪瘑" prop="modelKey"> + <el-input v-model="form.modelKey" clearable disabled placeholder="璇疯緭鍏ユā鍨嬫爣璇�" /> + </el-form-item> + <el-form-item label="妯″瀷鍚嶇О" prop="modelName"> + <el-input v-model="form.modelName" clearable :disabled="form.modelId !== undefined" placeholder="璇疯緭鍏ユā鍨嬪悕绉�" /> + </el-form-item> + <el-form-item label="娴佺▼鍒嗙被" prop="category"> + <el-select v-model="form.category" placeholder="璇烽�夋嫨" clearable style="width:100%"> + <el-option v-for="item in categoryOptions" :key="item.categoryId" :label="item.categoryName" :value="item.code" /> + </el-select> + </el-form-item> + <el-form-item label="鎻忚堪" prop="description"> + <el-input v-model="form.description" type="textarea" placeholder="璇疯緭鍏ュ唴瀹�" maxlength="200" show-word-limit /> + </el-form-item> + </el-form> + <template #footer> + <div class="dialog-footer"> + <el-button type="primary" @click="submitForm">纭� 瀹�</el-button> + <el-button @click="cancel">鍙� 娑�</el-button> + </div> + </template> + </el-dialog> + + <el-dialog :title="designer.title" v-model="designer.visible" append-to-body fullscreen> + <ProcessDesigner + :key="`designer-${reloadIndex}`" + ref="modelDesignerRef" + v-loading="designerLoading" + :designer-form="designerForm" + :bpmn-xml="bpmnXml" + @save="onSaveDesigner" + /> + </el-dialog> + + <el-dialog :title="history.title" v-model="history.visible" append-to-body> + <el-table v-loading="historyLoading" :data="historyList" @selection-change="handleSelectionChange"> + <el-table-column type="selection" width="55" align="center" /> + <el-table-column label="妯″瀷鏍囪瘑" align="center" prop="modelKey" :show-overflow-tooltip="true" /> + <el-table-column label="妯″瀷鍚嶇О" align="center" prop="modelName" :show-overflow-tooltip="true" /> + <el-table-column label="娴佺▼鍒嗙被" align="center" prop="categoryName" :formatter="categoryFormat" /> + <el-table-column label="妯″瀷鐗堟湰" align="center"> + <template #default="scope"> + <el-tag>v{{ scope.row.version }}</el-tag> + </template> + </el-table-column> + <el-table-column label="鎻忚堪" align="center" prop="description" :show-overflow-tooltip="true" /> + <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime" width="180" /> + <el-table-column label="鎿嶄綔" width="180" align="center" class-name="small-padding fixed-width"> + <template #default="scope"> + <el-tooltip content="閮ㄧ讲" placement="top"> + <el-button link type="primary" icon="Promotion" @click="handleDeploy(scope.row)" v-hasPermi="['workflow:model:deploy']"></el-button> + </el-tooltip> + <el-tooltip content="璁句负鏈�鏂�" placement="top"> + <el-button link type="primary" icon="Star" @click="handleLatest(scope.row)" v-hasPermi="['workflow:model:save']"></el-button> + </el-tooltip> + </template> + </el-table-column> + </el-table> + + <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> + </el-dialog> + + <!-- 娴佺▼鍥� --> + <el-dialog :title="processDialog.title" v-model="processDialog.visible" width="70%"> + <ProcessViewer :key="`designer-${reloadIndex}`" :xml="processXml" :style="{height: '650px'}" /> + </el-dialog> + </div> +</template> + +<script setup lang="ts"> +import { getBpmnXml, listModel, historyModel, latestModel, addModel, updateModel, saveModel, delModel, deployModel, getModel } from "#/api/workflow/model"; +import { listAllCategory } from "#/api/workflow/category"; + +import {getCurrentInstance,ref,reactive,toRefs,onMounted} from "vue"; +import {useRouter} from "vue-router"; +import { defineAsyncComponent } from "vue"; +// 寮傛鍔犺浇缁勪欢 +const ProcessDesigner = defineAsyncComponent(() => + import('#/components/ProcessDesigner/index.vue') +); +const ProcessViewer = defineAsyncComponent(() => + import('#/components/ProcessViewer/index.vue') +); +const { proxy } = getCurrentInstance() ; + +const modelList = ref([]); +const loading = ref(true); +const showSearch = ref(true); +const ids = ref([]); +const single = ref(true); +const multiple = ref(true); +const total = ref(0); +const categoryOptions = ref([]); +const designerLoading = ref(true); +const bpmnXml = ref(''); +const reloadIndex = ref(0); +const processXml = ref(""); + +const historyList = ref([]); +const historyLoading = ref(true); +const historyTotal = ref(0); + +const modelFormRef = ref(); +const queryFormRef = ref(); +const modelDesignerRef = ref(null) + +const dialog = reactive({ + visible: false, + title: '' +}); + +const processDialog = reactive({ + visible: false, + title: '娴佺▼鍥�' +}); + +const designer = reactive({ + visible: false, + title: '' +}); + +const history = reactive({ + visible: false, + title: '' +}); + +const initFormData={ + modelId: undefined, + modelKey: `Process_${new Date().getTime()}`, + modelName: `涓氬姟娴佺▼_${new Date().getTime()}`, + category: '', + description: '', + formType: undefined, + formId: undefined, + bpmnXml: '', + newVersion: false +} + +const data = reactive({ + form: {...initFormData}, + queryParams: { + pageNum: 1, + pageSize: 10, + modelKey: '', + modelName: '', + category: '' + }, + rules: { + modelKey: [{ required: true, message: "宀椾綅鍚嶇О涓嶈兘涓虹┖", trigger: "blur" }], + modelName: [{ required: true, message: "宀椾綅缂栫爜涓嶈兘涓虹┖", trigger: "blur" }], + } +}); + +const designerForm = reactive({ + modelId: '', + form: { + processName: '', + processKey: '' + } +}); + +const { queryParams, form, rules } = toRefs(data); + +const router = useRouter(); + +/** 鏌ヨ妯″瀷鍒楄〃 */ +const getList = async () => { + // loading.value = true; + const res = await listModel(queryParams.value); + console.log(res) + modelList.value = res.rows; + total.value = res.total; + loading.value = false; +} +/** 鍙栨秷鎸夐挳 */ +const cancel = () => { + reset(); + dialog.visible = false; +} +/** 琛ㄥ崟閲嶇疆 */ +const reset = () => { + form.value = {...initFormData}; + modelFormRef.value.resetFields(); +} +/** 鎼滅储鎸夐挳鎿嶄綔 */ +const handleQuery = () => { + queryParams.value.pageNum = 1; + getList(); +} +/** 閲嶇疆鎸夐挳鎿嶄綔 */ +const resetQuery = () => { + queryFormRef.value.resetFields(); + handleQuery(); +} +/** 澶氶�夋閫変腑鏁版嵁 */ +const handleSelectionChange = (selection) => { + ids.value = selection.map(item => item.modelId); + single.value = selection.length != 1; + multiple.value = !selection.length; +} +/** 鏂板鎸夐挳鎿嶄綔 */ +const handleAdd = () => { + dialog.visible = true; + dialog.title = "娣诲姞妯″瀷"; + nextTick(() => { + reset(); + }) +} +/** 淇敼鎸夐挳鎿嶄綔 */ +const handleUpdate = (row) => { + dialog.visible = true; + dialog.title = "淇敼妯″瀷"; + nextTick(async () => { + reset(); + const modelId = row.modelId || ids.value[0]; + const res = await getModel(modelId); + form.value = res.data; + }); +}; +/** 鍒犻櫎鎸夐挳鎿嶄綔 */ +const handleDelete = async (row) => { + const modelIds = row.modelId || ids.value; + await proxy?.$modal.confirm('鏄惁纭鍒犻櫎鍙傛暟缂栧彿涓�"' + modelIds + '"鐨勬暟鎹」锛�'); + await delModel(modelIds); + getList(); + proxy?.$modal.msgSuccess("鍒犻櫎鎴愬姛"); +} +/** 瀵煎嚭鎸夐挳鎿嶄綔 */ +const handleExport = () => { + proxy?.download("workflow/model/export", { + ...queryParams.value + }, `model_${new Date().getTime()}.xlsx`); +}; +/** 鏌ョ湅娴佺▼鍥� */ +const handleProcessView = async (row) => { + reloadIndex.value++; + // 鍙戦�佽姹傦紝鑾峰彇xml + const res = await getBpmnXml(row.modelId); + processXml.value = res.data; + processDialog.visible = true; +} +/** 璁捐鎸夐挳鎿嶄綔 */ +const handleDesigner = async (row) => { + reloadIndex.value++; + designerForm.modelId = row.modelId; + const res = await getBpmnXml(row.modelId); + bpmnXml.value = res.data || ''; + designerLoading.value = false; + designer.title = "娴佺▼璁捐 - " + row.modelName; + designer.visible = true; +} +const handleDeploy = (row) => { + loading.value = true; + nextTick(async () => { + await deployModel({ modelId: row?.modelId }); + proxy.$modal.msgSuccess("鎿嶄綔鎴愬姛"); + router.push({ + name: 'Deploy', + path: '/workflow/deploy' + }); + loading.value = false; + }); +} +const handleLatest = async (row) => { + await proxy.$modal.confirm('鏄惁灏嗘妯″瀷淇濆瓨涓烘柊鐗堟湰?'); + historyLoading.value = true; + await latestModel({modelId: row.modelId}); + history.visible = false; + getList(); + proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛"); + historyLoading.value = false; +} +/** 鏌ヨ鍘嗗彶鍒楄〃 */ +const getHistoryList = async () => { + historyLoading.value = true; + const res = await historyModel(queryParams.value); + historyList.value = res.rows; + historyTotal.value = res.total; + historyLoading.value = false; +} +const handleHistory = (row) => { + history.visible = true; + history.title = "妯″瀷鍘嗗彶"; + queryParams.value.modelKey = row?.modelKey; + getHistoryList(); +} +/** 鎻愪氦琛ㄥ崟鎿嶄綔 */ +const submitForm = () => { + modelFormRef.value.validate(async (valid) => { + if (valid) { + form.value.modelId ? await updateModel(form.value) : await addModel(form.value); + proxy.$modal.msgSuccess("鎿嶄綔鎴愬姛"); + dialog.visible = false; + getList(); + } + }) +} +/** 鏌ヨ娴佺▼鍒嗙被鍒楄〃 */ +const getCategoryList = async () => { + const res = await listAllCategory(); + categoryOptions.value = res.data; +} + +const onSaveDesigner = async (str) => { + bpmnXml.value = str; + let dataBody = { + modelId: designerForm.modelId, + bpmnXml: str + } + proxy.$modal.confirm('鏄惁灏嗘妯″瀷淇濆瓨涓烘柊鐗堟湰?').then(() => { + confirmSave(dataBody, true) + }).catch(action => { + if (action === 'cancel') { + confirmSave(dataBody, false) + } + }) +} +const confirmSave = async (body, newVersion) => { + designerLoading.value = true; + console.log(body,"body"); + await saveModel(Object.assign(body, { newVersion: newVersion })); + getList(); + proxy.$modal.msgSuccess("淇濆瓨鎴愬姛"); + designerLoading.value = false; + designer.visible = false; +} + +const categoryFormat = (row) => { + var category = categoryOptions.value.find(function(k) { + return k.code === row.category; + }); + return category ? category.categoryName : ''; +} + +onMounted(() => { + getCategoryList() + getList(); +}); +</script> + +<style lang="scss" scoped> +.el-dialog__body { + max-height: calc(100vh) !important; + overflow-y: auto; + overflow-x: hidden; +} +</style> diff --git a/ruoyi-ui/apps/web-antd/src/views/workflow/work/claim.vue b/ruoyi-ui/apps/web-antd/src/views/workflow/work/claim.vue new file mode 100644 index 0000000..a272b65 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/views/workflow/work/claim.vue @@ -0,0 +1,129 @@ +<template> + <div class="app-container"> + <div class="search" v-show="showSearch"> + <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="70"> + <el-form-item label="娴佺▼鍚嶇О" prop="processName"> + <el-input v-model="queryParams.processName" placeholder="璇疯緭鍏ユ祦绋嬪悕绉�" clearable style="width: 200px" @keyup.enter="handleQuery" /> + </el-form-item> + <el-form-item label="娴佺▼鍒嗙被" prop="category"> + <el-select v-model="queryParams.category" clearable placeholder="璇烽�夋嫨" style="width: 240px"> + <el-option v-for="item in categoryOptions" :key="item.categoryId" :label="item.categoryName" :value="item.code" /> + </el-select> + </el-form-item> + <el-form-item label="鎺ユ敹鏃堕棿" style="width: 308px;"> + <el-date-picker + v-model="dateRange" + value-format="YYYY-MM-DD" + type="daterange" + range-separator="-" + start-placeholder="寮�濮嬫棩鏈�" + end-placeholder="缁撴潫鏃ユ湡" + ></el-date-picker> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button> + <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button> + </el-form-item> + </el-form> + </div> + <el-card shadow="never"> + <template #header> + <el-row :gutter="10" class="mb8"> + <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> + </el-row> + </template> + + + <el-table v-loading="loading" :data="claimList"> + <el-table-column type="selection" width="55" align="center" /> + <el-table-column label="浠诲姟缂栧彿" align="center" prop="taskId" :show-overflow-tooltip="true" /> + <el-table-column label="娴佺▼鍚嶇О" align="center" prop="procDefName" /> + <el-table-column label="浠诲姟鑺傜偣" align="center" prop="taskName" /> + <el-table-column label="娴佺▼鐗堟湰" align="center"> + <template #default="scope"> + <el-tag>v{{scope.row.procDefVersion}}</el-tag> + </template> + </el-table-column> + <el-table-column label="娴佺▼鍙戣捣浜�" align="center" prop="startUserName" /> + <el-table-column label="鎺ユ敹鏃堕棿" align="center" prop="createTime" width="180" /> + <el-table-column label="鎿嶄綔" width="180" align="center" class-name="small-padding fixed-width"> + <template #default="scope"> + <el-tooltip content="绛炬敹" placement="top"> + <el-button link type="primary" icon="EditPen" @click="handleClaim(scope.row)" v-hasPermi="['workflow:process:claim']"></el-button> + </el-tooltip> + </template> + </el-table-column> + </el-table> + + <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> + </el-card> + </div> +</template> + +<script setup name="Claim" lang="js"> +import { listClaimProcess } from "#/api/workflow/work/process"; +import { claimTask } from "#/api/workflow/work/task"; + +import { listAllCategory } from "#/api/workflow/category"; + +const router = useRouter(); +const { proxy } = getCurrentInstance() ; + +const claimList = ref([]); +const loading = ref(true); +const showSearch = ref(true); +const ids = ref([]); +const single = ref(true); +const multiple = ref(true); +const total = ref(0); +const dateRange = ref(['','']); +const categoryOptions = ref([]); +const queryFormRef = ref(); + +const queryParams = ref({ + pageNum: 1, + pageSize: 10, + processName: '' +}); + +/** 鏌ヨ娴佺▼鍒嗙被鍒楄〃 */ +const getCategoryList = async () => { + const res = await listAllCategory(); + categoryOptions.value = res.data; +} +/** 鏌ヨ寰呭姙鍒楄〃 */ +const getList = async () => { + loading.value = true; + const res = await listClaimProcess(proxy.addDateRange(queryParams.value, dateRange.value)); + claimList.value = res.rows; + total.value = res.total; + loading.value = false; +} +/** 鎼滅储鎸夐挳鎿嶄綔 */ +const handleQuery = () => { + queryParams.value.pageNum = 1; + getList(); +} +/** 閲嶇疆鎸夐挳鎿嶄綔 */ +const resetQuery = () => { + queryFormRef.value.resetFields(); + handleQuery(); +} +/** 绛炬敹鎸夐挳鎿嶄綔 */ +const handleClaim = async (row) => { + const res = await claimTask({ taskId: row.taskId }) + proxy.$modal.msgSuccess(res.msg); + router.push({ path: '/work/todo' }) +} + +const categoryFormat = (row) => { + var category = categoryOptions.value.find(function(k) { + return k.code === row.category; + }); + return category && category.categoryName ? category.categoryName : ''; +} +onMounted(() => { + getCategoryList(); + getList(); +}); +</script> diff --git a/ruoyi-ui/apps/web-antd/src/views/workflow/work/copy.vue b/ruoyi-ui/apps/web-antd/src/views/workflow/work/copy.vue new file mode 100644 index 0000000..6fdfe88 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/views/workflow/work/copy.vue @@ -0,0 +1,122 @@ +<template> + <div class="app-container"> + <div class="search" v-show="showSearch"> + <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="70"> + <el-form-item label="娴佺▼鍚嶇О" prop="processName"> + <el-input v-model="queryParams.processName" placeholder="璇疯緭鍏ユ祦绋嬪悕绉�" clearable style="width: 200px" @keyup.enter="handleQuery" /> + </el-form-item> + <el-form-item label="娴佺▼鍒嗙被" prop="category"> + <el-select v-model="queryParams.category" clearable placeholder="璇烽�夋嫨" style="width: 240px"> + <el-option v-for="item in categoryOptions" :key="item.categoryId" :label="item.categoryName" :value="item.code" /> + </el-select> + </el-form-item> + <el-form-item label="鍙戣捣浜�" prop="originatorName"> + <el-input v-model="queryParams.originatorName" placeholder="璇疯緭鍏ュ彂璧蜂汉" clearable style="width: 200px" @keyup.enter="handleQuery" /> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button> + <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button> + </el-form-item> + </el-form> + </div> + <el-card shadow="never"> + <template #header> + <el-row :gutter="10" class="mb8"> + <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> + </el-row> + </template> + + <el-table v-loading="loading" :data="copyList"> + <el-table-column type="selection" width="55" align="center" /> + <el-table-column label="鎶勯�佺紪鍙�" align="center" prop="copyId" /> + <el-table-column label="鏍囬" align="center" prop="title" :show-overflow-tooltip="true" /> + <el-table-column label="娴佺▼鍚嶇О" align="center" prop="processName" :show-overflow-tooltip="true" /> + <el-table-column label="娴佺▼鍒嗙被" align="center" prop="categoryId" :formatter="categoryFormat" /> + <el-table-column label="鍙戣捣浜�" align="center" prop="originatorName" /> + <el-table-column label="鍒涘缓鏃堕棿" align="center" prop="createTime"> + <template #default="scope"> + <span>{{ parseTime(scope.row.createTime) }}</span> + </template> + </el-table-column> + <el-table-column label="鎿嶄綔" width="180" align="center" class-name="small-padding fixed-width"> + <template #default="scope"> + <el-tooltip content="璇︽儏" placement="top"> + <el-button link type="primary" icon="View" @click="handleDetails(scope.row)" v-hasPermi="['workflow:process:query']"></el-button> + </el-tooltip> + </template> + </el-table-column> + </el-table> + + <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> + </el-card> + </div> +</template> + +<script setup name="Copy" lang="js"> +import { listCopyProcess } from "#/api/workflow/work/process" +const router = useRouter(); +import { listAllCategory } from "#/api/workflow/category"; +const { proxy } = getCurrentInstance() ; + +const copyList = ref([]); +const loading = ref(true); +const showSearch = ref(true); +const ids = ref([]); +const single = ref(true); +const multiple = ref(true); +const total = ref(0); +const categoryOptions = ref([]); +const queryFormRef = ref(); + +const queryParams = ref({ + pageNum: 1, + pageSize: 10, + processName: "", + originatorName: "", + category: '' +}); + +/** 鏌ヨ娴佺▼鍒嗙被鍒楄〃 */ +const getCategoryList = async () => { + const res = await listAllCategory(); + categoryOptions.value = res.data; +} +/** 鏌ヨ寰呭姙鍒楄〃 */ +const getList = async () => { + loading.value = true; + const res = await listCopyProcess(queryParams.value); + copyList.value = res.rows; + total.value = res.total; + loading.value = false; +} +/** 鎼滅储鎸夐挳鎿嶄綔 */ +const handleQuery = () => { + queryParams.value.pageNum = 1; + getList(); +} +/** 閲嶇疆鎸夐挳鎿嶄綔 */ +const resetQuery = () => { + queryFormRef.value.resetFields(); + handleQuery(); +} +/** 娴佺▼璇︽儏 */ +const handleDetails = (row) => { + router.push({ + path: '/workflow/process/detail/' + row.instanceId, + query: { + processed: false + } + }) +} +const categoryFormat = (row) => { + var category = categoryOptions.value.find(function(find) { + return find.code === row.categoryId; + }); + return category && category.categoryName ? category.categoryName : ''; +} + +onMounted(() => { + getCategoryList(); + getList(); +}); +</script> diff --git a/ruoyi-ui/apps/web-antd/src/views/workflow/work/detail.vue b/ruoyi-ui/apps/web-antd/src/views/workflow/work/detail.vue new file mode 100644 index 0000000..c61e310 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/views/workflow/work/detail.vue @@ -0,0 +1,545 @@ +<template> + <div class="app-container"> + <el-tabs tab-position="top" :model-value="processed === true ? 'approval' : 'form'"> + <el-tab-pane label="浠诲姟鍔炵悊" name="approval" v-if="processed === true"> + <el-card class="box-card" shadow="hover" v-if="taskFormOpen"> + <template #header> + <span>濉啓琛ㄥ崟</span> + </template> + <div class="cu-content"> + <v-form-render :form-json="{}" :form-data="{}" ref="vfRenderRef" /> + </div> + </el-card> + <el-card class="box-card" shadow="hover"> + <template #header> + <span>瀹℃壒娴佺▼</span> + </template> + <el-row> + <el-col :span="20" :offset="2"> + <el-form ref="taskFormRef" :model="taskForm" :rules="rules" label-width="120px"> + <el-form-item label="瀹℃壒鎰忚" prop="comment"> + <el-input type="textarea" :rows="5" v-model="taskForm.comment" placeholder="璇疯緭鍏� 瀹℃壒鎰忚" /> + </el-form-item> + <el-form-item label="鎶勯�佷汉" prop="copyUserIds"> + <el-tag :key="index" v-for="(item, index) in copyUser" closable :disable-transitions="false" @close="handleClose('copy', item)"> + {{ item.nickName }} + </el-tag> + <el-button class="button-new-tag" type="primary" icon="el-icon-plus" circle @click="onSelectCopyUsers" /> + </el-form-item> + <el-form-item label="鎸囧畾瀹℃壒浜�" prop="copyUserIds"> + <el-tag :key="index" v-for="(item, index) in nextUser" closable :disable-transitions="false" @close="handleClose('next', item)"> + {{ item.nickName }} + </el-tag> + <el-button class="button-new-tag" type="primary" icon="el-icon-plus" circle @click="onSelectNextUsers" /> + </el-form-item> + </el-form> + </el-col> + </el-row> + <el-row :gutter="10" type="flex" justify="center"> + <el-col :span="1.5"> + <el-button icon="CircleCheck" type="success" @click="handleComplete">閫氳繃</el-button> + </el-col> + <el-col :span="1.5"> + <el-button icon="ChatLineSquare" type="primary" @click="handleDelegate">濮旀淳</el-button> + </el-col> + <el-col :span="1.5"> + <el-button icon="Switch" type="success" @click="handleTransfer">杞姙</el-button> + </el-col> + <el-col :span="1.5"> + <el-button icon="RefreshLeft" type="warning" @click="handleReturn">閫�鍥�</el-button> + </el-col> + <el-col :span="1.5"> + <el-button icon="CircleClose" type="danger" @click="handleReject">鎷掔粷</el-button> + </el-col> + </el-row> + </el-card> + </el-tab-pane> + + <el-tab-pane label="琛ㄥ崟淇℃伅" name="form"> + <div v-if="formVisible"> + <el-card class="box-card" shadow="never" v-for="(item, index) in processFormList" :key="index"> + <template #header> + <span>{{ item.title }}</span> + </template> + <!--娴佺▼澶勭悊琛ㄥ崟妯″潡--> + <div class="cu-content"> + <v-form-render :form-json="item.formModel" :form-data="item.formData" ref="vFormRenderRef" /> + </div> + </el-card> + </div> + </el-tab-pane> + + <el-tab-pane label="娴佽浆璁板綍" name="record"> + <el-card class="box-card" shadow="never"> + <el-col :span="20" :offset="2"> + <div class="block"> + <el-timeline> + <el-timeline-item v-for="(item, index) in historyProcNodeList" :key="index" :type="tagType(item.endTime)"> + <p style="font-weight: 700">{{ item.activityName }}</p> + <el-card v-if="item.activityType === 'startEvent'" class="box-card" shadow="hover"> + {{ item.assigneeName }} 鍦� {{ item.createTime }} 鍙戣捣娴佺▼ + </el-card> + <el-card v-if="item.activityType === 'userTask'" class="box-card" shadow="hover"> + <el-descriptions :column="5" :labelStyle="{'font-weight': 'bold'}"> + <el-descriptions-item label="瀹為檯鍔炵悊">{{ item.assigneeName || '-'}}</el-descriptions-item> + <el-descriptions-item label="鍊欓�夊姙鐞�">{{ item.candidate || '-'}}</el-descriptions-item> + <el-descriptions-item label="鎺ユ敹鏃堕棿">{{ item.createTime || '-'}}</el-descriptions-item> + <el-descriptions-item label="鍔炵粨鏃堕棿">{{ item.endTime || '-' }}</el-descriptions-item> + <el-descriptions-item label="鑰楁椂">{{ item.duration || '-'}}</el-descriptions-item> + </el-descriptions> + <div v-if="item.commentList && item.commentList.length > 0"> + <div v-for="(comment, index) in item.commentList" :key="index"> + <el-divider content-position="left"> + <el-tag :type="approveTypeTag(comment.type)">{{ commentType(comment.type) }}</el-tag> + <el-tag type="info" effect="plain">{{ parseTime(comment.time, '{y}-{m}-{d} {h}:{i}:{s}')}}</el-tag> + </el-divider> + <span>{{ comment.fullMessage }}</span> + </div> + </div> + </el-card> + <el-card v-if="item.activityType === 'endEvent'" class="box-card" shadow="hover"> + {{ item.createTime }} 缁撴潫娴佺▼ + </el-card> + </el-timeline-item> + </el-timeline> + </div> + </el-col> + </el-card> + </el-tab-pane> + + <el-tab-pane label="娴佺▼璺熻釜" name="track"> + <el-card class="box-card" shadow="never"> + <ProcessViewer + :key="`designer-${loadIndex}`" + :style="'height:' + height" + :xml="processXml" + :finishedInfo="finishedInfo" + :allCommentList="historyProcNodeList" + /> + </el-card> + </el-tab-pane> + </el-tabs> + + <!--閫�鍥炴祦绋�--> + <el-dialog :title="returnDialog.title" v-model="returnDialog.visible" width="40%" append-to-body> + <el-radio-group v-model="returnTaskKey"> + <el-radio-button v-for="item in returnTaskList" :key="item.id" :label="item.id"> + {{ item.name }} + </el-radio-button> + </el-radio-group> + <template #footer> + <el-button @click="returnDialog.visible = false">鍙� 娑�</el-button> + <el-button type="primary" @click="submitReturn">纭� 瀹�</el-button> + </template> + </el-dialog> + + <el-dialog :title="userSelectDialog.title" v-model="userSelectDialog.visible" width="60%" append-to-body> + <el-row type="flex" :gutter="20"> + <!--閮ㄩ棬鏁版嵁--> + <el-col :span="5"> + <el-card shadow="never" style="height: 100%"> + <template #header> + <span>閮ㄩ棬鍒楄〃</span> + </template> + <div class="head-container"> + <el-input v-model="deptName" placeholder="璇疯緭鍏ラ儴闂ㄥ悕绉�" prefix-icon="Search" clearable /> + <el-tree + :data="deptOptions" + :props="{ label: 'label', children: 'children' }" + :expand-on-click-node="false" + :filter-node-method="filterNode" + ref="deptTreeRef" + default-expand-all + @node-click="handleNodeClick" + /> + </div> + </el-card> + </el-col> + <el-col :span="18"> + <el-table + ref="userTable" + :key="userSelectType" + height="500" + v-loading="userLoading" + :data="userList" + highlight-current-row + @current-change="changeCurrentUser" + @selection-change="handleSelectionChange" + > + <el-table-column v-if="userSelectType === 'copy' || userSelectType === 'next'" width="55" type="selection" /> + <el-table-column v-else width="30"> + <template #default="scope"> + <el-radio :label="scope.row.userId" v-model="currentUserId">{{''}}</el-radio> + </template> + </el-table-column> + <el-table-column label="鐢ㄦ埛鍚嶇О" align="center" prop="userName" /> + <el-table-column label="鐢ㄦ埛鏄电О" align="center" prop="nickName" /> + <el-table-column label="鎵嬫満" align="center" prop="phonenumber" /> + </el-table> + <pagination :total="userTotal" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList(selectDeptId)" /> + </el-col> + </el-row> + <template #footer> + <el-button @click="userSelectDialog.visible = false">鍙� 娑�</el-button> + <el-button type="primary" @click="submitUserData">纭� 瀹�</el-button> + </template> + </el-dialog> + </div> +</template> + +<script setup name="Detail" lang="js"> +import { detailProcess } from "#/api/workflow/work/process"; +import { complete, delegate, transfer, rejectTask, returnList, returnTask } from "#/api/workflow/work/task"; +import { deptTreeSelect, selectUser } from "#/api/workflow/identity"; +import ProcessViewer from "#/components/ProcessViewer"; +import {useRoute} from "vue-router"; + +const route = useRoute(); +const router = useRouter(); +const { proxy } = getCurrentInstance() ; + +const userList = ref([]); +const processed = ref(false); +const taskFormOpen = ref(false) +const userMultipleSelection = ref([]); +const userSelectType = ref(); +const currentUserId = ref(); +const userLoading = ref(false); +const userTotal = ref(0); +const loadIndex = ref(0); +const height = ref(document.documentElement.clientHeight - 205 + 'px;'); +const processXml = ref(''); +const taskFormVisible = ref(false); +const processFormList = ref([]); +const taskFormData = ref([]); +const historyProcNodeList = ref(); +const formVisible = ref(false); +const finishedInfo = ref({}); + +const deptName = ref(''); +const selectDeptId = ref(''); +const deptOptions = ref([]); + +const returnTaskList = ref(); +const returnTaskKey = ref(); + +const copyUser = ref([]); +const nextUser = ref([]); + +const taskFormRef = ref(); +const vFormRenderRef = ref(null); +const deptTreeRef = ref(null); + +const returnDialog = reactive({ + visible: false, + title: '閫�鍥炴祦绋�' +}); + +const userSelectDialog = reactive({ + visible: false, + title: '' +}); + +const taskForm = reactive({ + comment: '', + procInsId: '', + taskId: '', + userId: '', + copyUserIds: '', + nextUserIds: '', + vars: '', + targetKey: '' +}); + +const rules = ref({ + comment: [{ required: true, message: '璇疯緭鍏ュ鎵规剰瑙�', trigger: 'blur' }] +}); + +const queryParams = ref({ + pageNum: 1, + pageSize: 10 +}); +const tagType = (val) => { + if (val) { + return "success"; + } else { + return "info"; + } +} +const commentType = (val) => { + switch (val) { + case '1': return '閫氳繃' + case '2': return '閫�鍥�' + case '3': return '椹冲洖' + case '4': return '濮旀淳' + case '5': return '杞姙' + case '6': return '缁堟' + case '7': return '鎾ゅ洖' + } +} +const approveTypeTag = (val) => { + switch (val) { + case '1': return 'success' + case '2': return 'warning' + case '3': return 'danger' + case '4': return 'primary' + case '5': return 'success' + case '6': return 'danger' + case '7': return 'info' + } +} + +const initData = () => { + taskForm.procInsId = route.params && route.params.procInsId ; + taskForm.taskId = route.query && route.query.taskId ; + processed.value = route.query && (route.query.processed || false) === "true"; + + // 娴佺▼浠诲姟閲嶈幏鍙栧彉閲忚〃鍗� + getProcessDetails(taskForm.procInsId, taskForm.taskId); + loadIndex.value++; +}; +/** 閫氳繃鏉′欢杩囨护鑺傜偣 */ +const filterNode = (value, data) => { + if (!value) return true + return data.label.includes(value) +} + /** 鏍规嵁鍚嶇О绛涢�夐儴闂ㄦ爲 */ +watch(deptName, (val) => { + deptTreeRef.value.filter(val) +}) +// 鑺傜偣鍗曞嚮浜嬩欢 +const handleNodeClick = (data) => { + selectDeptId.value=data.id; + getList(data.id); +} +/** 鏌ヨ閮ㄩ棬涓嬫媺鏍戠粨鏋� */ +const getTreeSelect = async () => { + const res = await deptTreeSelect(); + deptOptions.value = res.data; +}; +/** 鏌ヨ鐢ㄦ埛鍒楄〃 */ +const getList = async (deptId) => { + userLoading.value = true; + const res = await selectUser( + { + deptId: deptId, + pageNum:queryParams.value.pageNum, + pageSize:queryParams.value.pageSize + }); + userLoading.value = false; + userList.value = res.rows; + userTotal.value = res.total; +} + +const getProcessDetails = async (procInsId, taskId) => { + const params = {procInsId: procInsId, taskId: taskId} + const res = await detailProcess(params); + const data = res.data; + processXml.value = data.bpmnXml; + processFormList.value = data.processFormList; + taskFormVisible.value = data.existTaskForm; + if (taskFormVisible.value) { + taskFormData.value = data.taskFormData; + } + historyProcNodeList.value = data.historyProcNodeList; + finishedInfo.value = data.flowViewer; + formVisible.value = true; + nextTick(() => { + processFormList.value.forEach((item, index) => { + if (item.disabled) { + vFormRenderRef.value[index].disableForm(); + } + }) + }) +} +const onSelectCopyUsers = () => { + userMultipleSelection.value = copyUser.value; + onSelectUsers('娣诲姞鎶勯�佷汉', 'copy') +} +const onSelectNextUsers = () => { + userMultipleSelection.value = nextUser; + onSelectUsers('鎸囧畾瀹℃壒浜�', 'next') +} +const onSelectUsers = (title, type) => { + userSelectType.value = type; + userSelectDialog.title = title; + userSelectDialog.visible = true; + getTreeSelect(); + getList() +} +/** 閫氳繃浠诲姟 */ +const handleComplete = () => { + // 鏍¢獙琛ㄥ崟 + taskFormRef.value.validate(async (valid) => { + if (valid) { + const res = await complete(taskForm) + proxy.$modal.msgSuccess(res.msg); + goBack(); + } + }); +} +/** 濮旀淳浠诲姟 */ +const handleDelegate = () => { + userSelectType.value = 'delegate'; + userSelectDialog.title = '濮旀淳浠诲姟' + userSelectDialog.visible = true; + getTreeSelect(); +} +/** 杞姙浠诲姟 */ +const handleTransfer = () => { + userSelectType.value = 'transfer'; + userSelectDialog.title = '杞姙浠诲姟'; + userSelectDialog.visible = true; + getTreeSelect(); +} +/** 閫�鍥炰换鍔� */ +const handleReturn = async () => { + // 鏍¢獙琛ㄥ崟 + taskFormRef.value.validate(async (valid) => { + if (valid) { + const res = await returnList(taskForm); + returnTaskList.value = res.data; + returnDialog.visible = true; + } + }); +} +/** 鎷掔粷浠诲姟 */ +const handleReject = async () => { + await proxy.$modal.confirm('鎷掔粷瀹℃壒鍗曟祦绋嬩細缁堟锛屾槸鍚︾户缁�?'); + await rejectTask(taskForm); + proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛"); + goBack(); +} + +/** 杩斿洖椤甸潰 */ +const goBack = () => { + // 鍏抽棴褰撳墠鏍囩椤靛苟杩斿洖涓婁釜椤甸潰 + proxy?.$tab.closePage(route); + router.back() +} +// 鍏抽棴鏍囩 +const handleClose = (type, tag) => { + let userObj = userMultipleSelection.value.find(item => item.userId === tag.id); + userMultipleSelection.value.splice(userMultipleSelection.value.indexOf(userObj), 1); + if (type === 'copy') { + copyUser.value = userMultipleSelection.value; + // 璁剧疆鎶勯�佷汉ID + if (copyUser.value && copyUser.value.length > 0) { + const val = copyUser.value.map(item => item.id); + taskForm.copyUserIds = val instanceof Array ? val.join(',') : val; + } else { + taskForm.copyUserIds = ''; + } + } else if (type === 'next') { + nextUser.value = userMultipleSelection.value; + // 璁剧疆鎶勯�佷汉ID + if (nextUser.value && nextUser.value.length > 0) { + const val = nextUser.value.map(item => item.id); + taskForm.nextUserIds = val instanceof Array ? val.join(',') : val; + } else { + taskForm.nextUserIds = ''; + } + } +} +const changeCurrentUser = (val) => { + // currentUserId = val.userId +} +const handleSelectionChange = (val) => { + userMultipleSelection.value=val; +} +const submitReturn = () => { + // 鏍¢獙琛ㄥ崟 + taskFormRef.value.validate(async (valid) => { + if (valid) { + if (!returnTaskKey) { + proxy.$modal.msgError("璇烽�夋嫨閫�鍥炶妭鐐癸紒"); + } + taskForm.targetKey = returnTaskKey.value; + const res = await returnTask(taskForm); + proxy.$modal.msgSuccess(res.msg); + goBack() + } + }); + console.log("taskForm => ", taskForm.targetKey); +} +const submitUserData = () => { + let type = userSelectType.value; + if (type === 'copy' || type === 'next') { + if (!userMultipleSelection || userMultipleSelection.value.length <= 0) { + proxy.$modal.msgError("璇烽�夋嫨鐢ㄦ埛"); + return false; + } + let userIds = userMultipleSelection.value.map(k => k.userId); + if (type === 'copy') { + // 璁剧疆鎶勯�佷汉ID淇℃伅 + copyUser.value = userMultipleSelection.value; + taskForm.copyUserIds = userIds instanceof Array ? userIds.join(',') : userIds; + } else if (type === 'next') { + // 璁剧疆涓嬩竴绾у鎵逛汉ID淇℃伅 + nextUser.value = userMultipleSelection.value; + taskForm.nextUserIds = userIds instanceof Array ? userIds.join(',') : userIds; + } + userSelectDialog.visible = false; + } else { + if (!taskForm.comment) { + proxy?.$modal.msgError("璇疯緭鍏ュ鎵规剰瑙�"); + return false; + } + if (!currentUserId.value) { + proxy?.$modal.msgError("璇烽�夋嫨鐢ㄦ埛"); + return false; + } + taskForm.userId = currentUserId.value; + if (type === 'delegate') { + delegate(taskForm).then(res => { + proxy?.$modal.msgSuccess(res.msg); + goBack(); + }); + } + if (type === 'transfer') { + transfer(taskForm).then(res => { + proxy?.$modal.msgSuccess(res.msg); + goBack(); + }); + } + } +} + +onMounted(() => { + initData(); +}); +</script> + +<style lang="scss" scoped> +.clearfix:before, +.clearfix:after { + display: table; + content: ""; +} +.clearfix:after { + clear: both +} + +.box-card { + width: 100%; + margin-bottom: 20px; +} + +.el-tag + .el-tag { + margin-left: 10px; +} + +.el-row { + margin-bottom: 20px; + &:last-child { + margin-bottom: 0; + } +} +.el-col { + border-radius: 4px; +} + +.button-new-tag { + margin-left: 10px; +} +</style> diff --git a/ruoyi-ui/apps/web-antd/src/views/workflow/work/finished.vue b/ruoyi-ui/apps/web-antd/src/views/workflow/work/finished.vue new file mode 100644 index 0000000..540d2ca --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/views/workflow/work/finished.vue @@ -0,0 +1,140 @@ +<template> + <div class="app-container"> + <div class="search" v-show="showSearch"> + <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="70"> + <el-form-item label="娴佺▼鍚嶇О" prop="processName"> + <el-input v-model="queryParams.processName" placeholder="璇疯緭鍏ユ祦绋嬪悕绉�" clearable style="width: 200px" @keyup.enter="handleQuery" /> + </el-form-item> + <el-form-item label="娴佺▼鍒嗙被" prop="category"> + <el-select v-model="queryParams.category" clearable placeholder="璇烽�夋嫨" style="width: 240px"> + <el-option v-for="item in categoryOptions" :key="item.categoryId" :label="item.categoryName" :value="item.code" /> + </el-select> + </el-form-item> + <el-form-item label="瀹℃壒鏃堕棿" style="width: 308px;"> + <el-date-picker + v-model="dateRange" + value-format="YYYY-MM-DD" + type="daterange" + range-separator="-" + start-placeholder="寮�濮嬫棩鏈�" + end-placeholder="缁撴潫鏃ユ湡" + ></el-date-picker> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button> + <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button> + </el-form-item> + </el-form> + </div> + <el-card shadow="never"> + <template #header> + <el-row :gutter="10" class="mb8"> + <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> + </el-row> + </template> + + <el-table v-loading="loading" :data="finishedList"> + <el-table-column type="selection" width="55" align="center" /> + <el-table-column label="浠诲姟缂栧彿" align="center" prop="taskId" :show-overflow-tooltip="true" /> + <el-table-column label="娴佺▼鍚嶇О" align="center" prop="procDefName" :show-overflow-tooltip="true" /> + <el-table-column label="娴佺▼鍒嗙被" align="center" prop="category" :formatter="categoryFormat" /> + <el-table-column label="浠诲姟鑺傜偣" align="center" prop="taskName" /> + <el-table-column label="娴佺▼鍙戣捣浜�" align="center" prop="startUserName" /> + <el-table-column label="鎺ユ敹鏃堕棿" align="center" prop="createTime" width="180" /> + <el-table-column label="瀹℃壒鏃堕棿" align="center" prop="finishTime" width="180" /> + <el-table-column label="鑰楁椂" align="center" prop="duration" width="180" /> + <el-table-column label="鎿嶄綔" width="180" align="center" class-name="small-padding fixed-width"> + <template #default="scope"> + <el-tooltip content="璇︽儏" placement="top"> + <el-button link type="primary" icon="View" @click="handleDetails(scope.row)" v-hasPermi="['workflow:process:query']"></el-button> + </el-tooltip> + <el-tooltip content="鎾ゅ洖" placement="top"> + <el-button link type="primary" icon="RefreshLeft" @click="handleRevoke(scope.row)" v-hasPermi="['workflow:process:revoke']"></el-button> + </el-tooltip> + </template> + </el-table-column> + </el-table> + + <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> + </el-card> + </div> +</template> + +<script setup name="Finished" lang="js"> +import { listFinishedProcess } from "#/api/workflow/work/process"; +import { revokeProcess } from "#/api/workflow/work/task"; + +import { listAllCategory } from "#/api/workflow/category"; +const router = useRouter(); +const { proxy } = getCurrentInstance() ; + +const finishedList = ref([]); +const loading = ref(true); +const showSearch = ref(true); +const ids = ref([]); +const single = ref(true); +const multiple = ref(true); +const total = ref(0); +const dateRange = ref(['','']); +const categoryOptions = ref([]); +const queryFormRef = ref(); + +const queryParams = ref({ + pageNum: 1, + pageSize: 10, + processName: '', + category: '' +}); +/** 鏌ヨ娴佺▼鍒嗙被鍒楄〃 */ +const getCategoryList = async () => { + const res = await listAllCategory(); + categoryOptions.value = res.data; +} +/** 鏌ヨ寰呭姙鍒楄〃 */ +const getList = async () => { + loading.value = true; + const res = await listFinishedProcess(proxy.addDateRange(queryParams.value, dateRange.value)); + finishedList.value = res.rows; + total.value = res.total; + loading.value = false; +} +/** 鎼滅储鎸夐挳鎿嶄綔 */ +const handleQuery = () => { + queryParams.value.pageNum = 1; + getList(); +} +/** 閲嶇疆鎸夐挳鎿嶄綔 */ +const resetQuery = () => { + queryFormRef.value.resetFields(); + handleQuery(); +} +/** 娴佺▼娴佽浆璁板綍 */ +const handleDetails = (row) => { + router.push({ + path: '/workflow/process/detail/' + row.procInsId, + query: { + processed: false + } + }) +} +/** 鎾ゅ洖浠诲姟 */ +const handleRevoke = async (row) => { + const params = { + procInsId: row.procInsId, + taskId: row.taskId + }; + const res = await revokeProcess(params); + proxy.$modal.msgSuccess(res.msg); + getList(); +} +const categoryFormat = (row) => { + var category = categoryOptions.value.find(function(find) { + return find.code === row.category; + }); + return category && category.categoryName ? category.categoryName : ''; +} +onMounted(() => { + getCategoryList(); + getList(); +}); +</script> diff --git a/ruoyi-ui/apps/web-antd/src/views/workflow/work/index.vue b/ruoyi-ui/apps/web-antd/src/views/workflow/work/index.vue new file mode 100644 index 0000000..54f6e61 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/views/workflow/work/index.vue @@ -0,0 +1,158 @@ +<template> + <div class="app-container"> + <div class="search" v-show="showSearch"> + <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="70"> + <el-form-item label="娴佺▼鏍囪瘑" prop="processKey"> + <el-input v-model="queryParams.processKey" placeholder="璇疯緭鍏ユ祦绋嬫爣璇�" clearable style="width: 200px" @keyup.enter="handleQuery" /> + </el-form-item> + <el-form-item label="娴佺▼鍚嶇О" prop="processName"> + <el-input v-model="queryParams.processName" placeholder="璇疯緭鍏ユ祦绋嬪悕绉�" clearable style="width: 200px" @keyup.enter="handleQuery" /> + </el-form-item> + <el-form-item label="娴佺▼鍒嗙被" prop="category"> + <el-select v-model="queryParams.category" clearable placeholder="璇烽�夋嫨" style="width: 240px"> + <el-option v-for="item in categoryOptions" :key="item.categoryId" :label="item.categoryName" :value="item.code" /> + </el-select> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button> + <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button> + </el-form-item> + </el-form> + </div> + <el-card shadow="never"> + <template #header> + <el-row :gutter="10" class="mb8"> + <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> + </el-row> + </template> + + <el-table v-loading="loading" :data="processList"> + <el-table-column label="娴佺▼鏍囪瘑" align="center" prop="processKey" :show-overflow-tooltip="true" /> + <el-table-column label="娴佺▼鍚嶇О" align="center" :show-overflow-tooltip="true"> + <template #default="scope"> + <a @click="handleProcessView(scope.row)" class="link-type"> + <span>{{ scope.row.processName }}</span> + </a> + </template> + </el-table-column> + <el-table-column label="娴佺▼鍒嗙被" align="center" prop="categoryName" :formatter="categoryFormat" /> + <el-table-column label="娴佺▼鐗堟湰" align="center"> + <template #default="scope"> + <el-tag>v{{ scope.row.version }}</el-tag> + </template> + </el-table-column> + <el-table-column label="鐘舵��" align="center"> + <template #default="scope"> + <el-tag type="success" v-if="!scope.row.suspended">婵�娲�</el-tag> + <el-tag type="warning" v-if="scope.row.suspended">鎸傝捣</el-tag> + </template> + </el-table-column> + <el-table-column label="閮ㄧ讲鏃堕棿" align="center" prop="deploymentTime" width="180" > + <template #default="scope"> + {{ parseTime(scope.row.deploymentTime, '{y}-{m}-{d} {h}:{i}:{s}')}} + </template> + </el-table-column> + <el-table-column label="鎿嶄綔" width="180" align="center" class-name="small-padding fixed-width"> + <template #default="scope"> + <el-tooltip content="鍙戣捣" placement="top"> + <el-button link type="primary" icon="VideoPlay" @click="handleStart(scope.row)" v-hasPermi="['workflow:process:start']"></el-button> + </el-tooltip> + </template> + </el-table-column> + </el-table> + + <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> + </el-card> + + <!-- 娴佺▼鍥� --> + <el-dialog :title="processDialog.title" v-model="processDialog.visible" width="70%"> + <ProcessViewer :key="`designer-${reloadIndex}`" :xml="processXml" :style="{height: '650px'}" /> + </el-dialog> + </div> +</template> + +<script setup name="WorkProcess" lang="js"> +import { listProcess, getBpmnXml } from "#/api/workflow/work/process"; +import { listAllCategory } from "#/api/workflow/category"; +import ProcessViewer from "#/components/ProcessViewer/index.vue"; + +const router = useRouter(); +const { proxy } = getCurrentInstance() ; + +const processList = ref([]); +const loading = ref(true); +const showSearch = ref(true); +const total = ref(0); +const reloadIndex = ref(0); +const processXml = ref(""); +const categoryOptions = ref([]); + +const queryFormRef = ref(); + +const processDialog = reactive({ + visible: false, + title: '娴佺▼鍥�' +}); + +const queryParams = ref({ + pageNum: 1, + pageSize: 10, + processKey: '', + processName: '', + category: '' +}); + +/** 鏌ヨ娴佺▼鍒嗙被鍒楄〃 */ +const getCategoryList = async () => { + const res = await listAllCategory(); + categoryOptions.value = res.data; +} +/** 鏌ヨ娴佺▼鍒楄〃 */ +const getList = async () => { + loading.value = true; + const res = await listProcess(queryParams.value); + processList.value = res.rows; + total.value = res.total; + loading.value = false; +} +/** 鎼滅储鎸夐挳鎿嶄綔 */ +const handleQuery = () => { + queryParams.value.pageNum = 1; + getList(); +} +/** 閲嶇疆鎸夐挳鎿嶄綔 */ +const resetQuery = () => { + queryFormRef.value.resetFields(); + handleQuery(); +} +/** 鏌ョ湅娴佺▼鍥� */ +const handleProcessView = async (row) => { + reloadIndex.value++; + // 鍙戦�佽姹傦紝鑾峰彇xml + const res = await getBpmnXml(row.definitionId); + console.log(res.data); + processXml.value = res.data; + processDialog.visible = true; +} +/** 鍙戣捣娴佺▼ */ +const handleStart = (row) => { + router.push({ + path: '/workflow/process/start/' + row.deploymentId, + query: { + definitionId: row.definitionId, + } + }) +}; + +const categoryFormat = (row) => { + var category = categoryOptions.value.find(function(k) { + return k.code === row.category; + }); + return category ? category.categoryName : ''; +} + +onMounted(() => { + getCategoryList(); + getList(); +}); +</script> diff --git a/ruoyi-ui/apps/web-antd/src/views/workflow/work/own.vue b/ruoyi-ui/apps/web-antd/src/views/workflow/work/own.vue new file mode 100644 index 0000000..a3add22 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/views/workflow/work/own.vue @@ -0,0 +1,193 @@ +<template> + <div class="app-container"> + <div class="search" v-show="showSearch"> + <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="70"> + <el-form-item label="娴佺▼鏍囪瘑" prop="processKey"> + <el-input v-model="queryParams.processKey" placeholder="璇疯緭鍏ユ祦绋嬫爣璇�" clearable style="width: 200px" @keyup.enter="handleQuery" /> + </el-form-item> + <el-form-item label="娴佺▼鍚嶇О" prop="processName"> + <el-input v-model="queryParams.processName" placeholder="璇疯緭鍏ユ祦绋嬪悕绉�" clearable style="width: 200px" @keyup.enter="handleQuery" /> + </el-form-item> + <el-form-item label="娴佺▼鍒嗙被" prop="category"> + <el-select v-model="queryParams.category" clearable placeholder="娴佺▼鍒嗙被" style="width: 240px"> + <el-option v-for="item in categoryOptions" :key="item.categoryId" :label="item.categoryName" :value="item.code" /> + </el-select> + </el-form-item> + <el-form-item label="鎻愪氦鏃堕棿" style="width: 308px;"> + <el-date-picker + v-model="dateRange" + value-format="YYYY-MM-DD" + type="daterange" + range-separator="-" + start-placeholder="寮�濮嬫棩鏈�" + end-placeholder="缁撴潫鏃ユ湡" + ></el-date-picker> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button> + <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button> + </el-form-item> + </el-form> + </div> + <el-card shadow="never"> + <template #header> + <el-row :gutter="10" class="mb8"> + <el-col :span="1.5"> + <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['workflow:process:remove']"> + 鍒犻櫎 + </el-button> + </el-col> + <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> + </el-row> + </template> + + <el-table v-loading="loading" :data="ownProcessList" @selection-change="handleSelectionChange"> + <el-table-column type="selection" width="55" align="center" /> + <el-table-column label="娴佺▼缂栧彿" align="center" prop="procInsId" :show-overflow-tooltip="true" /> + <el-table-column label="娴佺▼鍚嶇О" align="center" prop="procDefName" :show-overflow-tooltip="true" /> + <el-table-column label="娴佺▼绫诲埆" align="center" prop="category" :formatter="categoryFormat" /> + <el-table-column label="娴佺▼鐗堟湰" align="center" width="80px"> + <template #default="scope"> + <el-tag>v{{ scope.row.procDefVersion }}</el-tag> + </template> + </el-table-column> + <el-table-column label="褰撳墠鑺傜偣" align="center" prop="taskName" /> + <el-table-column label="鎻愪氦鏃堕棿" align="center" prop="createTime" width="180" /> + <el-table-column label="娴佺▼鐘舵��" align="center" width="100"> + <template #default="scope"> + <dict-tag :options="wf_process_status" :value="scope.row.processStatus" /> + </template> + </el-table-column> + <el-table-column label="鑰楁椂" align="center" prop="duration" width="180" /> + <el-table-column label="鎿嶄綔" width="180" align="center" class-name="small-padding fixed-width"> + <template #default="scope"> + <el-tooltip content="璇︽儏" placement="top"> + <el-button link type="primary" icon="View" @click="handleDetails(scope.row)" v-hasPermi="['workflow:process:query']"></el-button> + </el-tooltip> + <el-tooltip content="鍙栨秷" placement="top"> + <el-button link type="primary" icon="CircleClose" @click="handleStop(scope.row)" v-hasPermi="['workflow:process:cancel']"></el-button> + </el-tooltip> + <el-tooltip content="鍒犻櫎" placement="top"> + <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['workflow:process:remove']"></el-button> + </el-tooltip> + </template> + </el-table-column> + </el-table> + + <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> + </el-card> + </div> +</template> + +<script setup name="Own" lang="js"> +import { listOwnProcess, stopProcess, delProcess } from "#/api/workflow/work/process"; +import { listAllCategory } from "#/api/workflow/category"; + + +const router = useRouter(); +const { proxy } = getCurrentInstance() ; +const { wf_process_status } = proxy.useDict("wf_process_status"); + + + + +const categoryOptions = ref([]); + +const ownProcessList = ref([]); +const loading = ref(true); +const showSearch = ref(true); +const ids = ref([]); +const single = ref(true); +const multiple = ref(true); +const total = ref(0); +const dateRange = ref(['','']); + +const queryFormRef = ref(); + +const queryParams = ref({ + pageNum: 1, + pageSize: 10, + processKey: '', + processName: '', + category: '' +}); + +/** 鏌ヨ娴佺▼鍒嗙被鍒楄〃 */ +const getCategoryList = async () => { + const res = await listAllCategory(); + categoryOptions.value = res.data; +}; +/** 鏌ヨ鎴戠殑娴佺▼鍒楄〃 */ +const getList = async () => { + loading.value = true; + const res = await listOwnProcess(proxy.addDateRange(queryParams.value, dateRange.value)); + ownProcessList.value = res.rows; + total.value = res.total; + loading.value = false; +} +/** 鎼滅储鎸夐挳鎿嶄綔 */ +const handleQuery = () => { + queryParams.value.pageNum = 1; + getList(); +} +/** 閲嶇疆鎸夐挳鎿嶄綔 */ +const resetQuery = () => { + queryFormRef.value.resetFields(); + handleQuery(); +} +/** 澶氶�夋閫変腑鏁版嵁 */ +const handleSelectionChange = (selection) => { + ids.value = selection.map(item => item.procInsId); + single.value = selection.length != 1; + multiple.value = !selection.length; +} +/** 娴佺▼璇︽儏 */ +const handleDetails = (row) => { + router.push({ + path: '/workflow/process/detail/' + row.procInsId, + query: { + processed: false + } + }) +} +/** 鍙栨秷娴佺▼鐢宠 */ +const handleStop = async (row) => { + await stopProcess( { procInsId: row.procInsId }); + proxy?.$modal.msgSuccess("鎿嶄綔鎴愬姛"); + getList(); +} +/** 鍐嶆鍙戣捣娴佺▼ */ +const handleAgain = (row) => { + // router.push({ + // path: '/workflow/process/start/' + row.deployId, + // query: { + // definitionId: row.procDefId, + // procInsId: row.procInsId + // } + // }) +} +/** 鍒犻櫎鎸夐挳鎿嶄綔 */ +const handleDelete = async (row) => { + const procInsIds = row.procInsId || ids.value; + await proxy.$modal.confirm('鏄惁纭鍒犻櫎娴佺▼瀹氫箟缂栧彿涓�"' + procInsIds + '"鐨勬暟鎹」?'); + await delProcess(procInsIds); + getList(); + proxy.$modal.msgSuccess("鍒犻櫎鎴愬姛"); +} + +/** 瀵煎嚭鎸夐挳鎿嶄綔 */ +const handleExport = () => { + proxy.download("workflow/process/ownExport", { + ...queryParams.value + }, `own_process_${new Date().getTime()}.xlsx`); +} + +const categoryFormat = (row) => { + return categoryOptions.value.find(k => k.code === row.category).categoryName ?categoryOptions.value.find(k => k.code === row.category).categoryName: ''; +} + +onMounted(() => { + getCategoryList(); + getList(); +}); +</script> diff --git a/ruoyi-ui/apps/web-antd/src/views/workflow/work/start.vue b/ruoyi-ui/apps/web-antd/src/views/workflow/work/start.vue new file mode 100644 index 0000000..73a2839 --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/views/workflow/work/start.vue @@ -0,0 +1,75 @@ +<template> + <div class="app-container"> + <el-card class="box-card"> + <template #header> + <span>鍙戣捣娴佺▼</span> + </template> + <div class="form-conf" v-if="dialog.visible"> + <v-form-render :form-json="formModel" :form-data="formData" ref="vfRenderRef"></v-form-render> + <div class="cu-submit"> + <el-button type="primary" @click="submit">鎻愪氦</el-button> + <el-button @click="reset">閲嶇疆</el-button> + </div> + </div> + </el-card> + </div> +</template> + +<script setup name="WorkStart" lang="js"> +import { getProcessForm, startProcess } from '#/api/workflow/work/process'; + + +const route = useRoute(); +const { proxy } = getCurrentInstance() ; + +const vfRenderRef = ref(null); + +const deployId = ref(); +const definitionId = ref(); +const formModel = ref({}); +const formData = ref({}); + +const dialog = reactive({ + visible: false, + title: '' +}); + +const initData = async () => { + deployId.value = route.params && route.params.deployId; + definitionId.value = route.query && route.query.definitionId; + const res = await getProcessForm({ definitionId: definitionId.value, deployId: deployId.value }); + formModel.value = res.data.formModel; + dialog.visible = true; + nextTick(async () => { + vfRenderRef.value.setFormJson(formModel.value || {formConfig: {}, widgetList: []}); + }); +} + +const submit = async () => { + const data = await vfRenderRef.value.getFormData(); + if (definitionId.value) { + const res = await startProcess(definitionId.value, JSON.stringify(data)); + proxy.$modal.msgSuccess(res.msg); + // const obj = { path: "/work/own" }; + // proxy?.$tab.closeOpenPage(obj); + proxy.$tab.closePage(); + proxy.$router.back(); + } +} + +const reset = () => { + vfRenderRef.value.resetForm(); +} + +onMounted(() => { + initData(); +}); +</script> + +<style lang="scss" scoped> +.form-conf { + margin: 15px auto; + width: 80%; + padding: 15px; +} +</style> diff --git a/ruoyi-ui/apps/web-antd/src/views/workflow/work/todo.vue b/ruoyi-ui/apps/web-antd/src/views/workflow/work/todo.vue new file mode 100644 index 0000000..092175d --- /dev/null +++ b/ruoyi-ui/apps/web-antd/src/views/workflow/work/todo.vue @@ -0,0 +1,132 @@ +<template> + <div class="app-container"> + <div class="search" v-show="showSearch"> + <el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="70"> + <el-form-item label="娴佺▼鍚嶇О" prop="processName"> + <el-input v-model="queryParams.processName" placeholder="璇疯緭鍏ユ祦绋嬪悕绉�" clearable style="width: 200px" @keyup.enter="handleQuery" /> + </el-form-item> + <el-form-item label="娴佺▼鍒嗙被" prop="category"> + <el-select v-model="queryParams.category" clearable placeholder="璇烽�夋嫨" style="width: 240px"> + <el-option v-for="item in categoryOptions" :key="item.categoryId" :label="item.categoryName" :value="item.code" /> + </el-select> + </el-form-item> + <el-form-item label="鎺ユ敹鏃堕棿" style="width: 308px;"> + <el-date-picker + v-model="dateRange" + value-format="YYYY-MM-DD" + type="daterange" + range-separator="-" + start-placeholder="寮�濮嬫棩鏈�" + end-placeholder="缁撴潫鏃ユ湡" + ></el-date-picker> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="Search" @click="handleQuery">鎼滅储</el-button> + <el-button icon="Refresh" @click="resetQuery">閲嶇疆</el-button> + </el-form-item> + </el-form> + </div> + <el-card shadow="never"> + <template #header> + <el-row :gutter="10" class="mb8"> + <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> + </el-row> + </template> + + <el-table v-loading="loading" :data="todoList"> + <el-table-column type="selection" width="55" align="center" /> + <el-table-column label="浠诲姟缂栧彿" align="center" prop="taskId" :show-overflow-tooltip="true" /> + <el-table-column label="娴佺▼鍚嶇О" align="center" prop="procDefName" /> + <el-table-column label="浠诲姟鑺傜偣" align="center" prop="taskName" /> + <el-table-column label="娴佺▼鍒嗙被" align="center" prop="category" :formatter="categoryFormat" /> + <el-table-column label="娴佺▼鐗堟湰" align="center"> + <template #default="scope"> + <el-tag>v{{scope.row.procDefVersion}}</el-tag> + </template> + </el-table-column> + <el-table-column label="娴佺▼鍙戣捣浜�" align="center" prop="startUserName" /> + <el-table-column label="鎺ユ敹鏃堕棿" align="center" prop="createTime" width="180" /> + <el-table-column label="鎿嶄綔" width="180" align="center" class-name="small-padding fixed-width"> + <template #default="scope"> + <el-tooltip content="鍔炵悊" placement="top"> + <el-button link type="primary" icon="EditPen" @click="handleProcess(scope.row)" v-hasPermi="['workflow:process:approval']"></el-button> + </el-tooltip> + </template> + </el-table-column> + </el-table> + + <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" /> + </el-card> + </div> +</template> + +<script setup name="Todo" lang="js"> +import { listTodoProcess } from '#/api/workflow/work/process'; + +import { listAllCategory } from "#/api/workflow/category"; +const router = useRouter(); +const { proxy } = getCurrentInstance() ; + +const todoList = ref([]); +const loading = ref(true); +const showSearch = ref(true); +const ids = ref([]); +const single = ref(true); +const multiple = ref(true); +const total = ref(0); +const dateRange = ref(['','']); +const categoryOptions = ref([]); +const queryFormRef = ref(); + +const queryParams = ref({ + pageNum: 1, + pageSize: 10, + processName: '', + category: '' +}); +/** 鏌ヨ娴佺▼鍒嗙被鍒楄〃 */ +const getCategoryList = async () => { + const res = await listAllCategory(); + categoryOptions.value = res.data; +} +/** 鏌ヨ寰呭姙鍒楄〃 */ +const getList = async () => { + loading.value = true; + const res = await listTodoProcess(proxy.addDateRange(queryParams.value, dateRange.value)); + todoList.value = res.rows; + total.value = res.total; + loading.value = false; +} +/** 鎼滅储鎸夐挳鎿嶄綔 */ +const handleQuery = () => { + queryParams.value.pageNum = 1; + getList(); +} +/** 閲嶇疆鎸夐挳鎿嶄綔 */ +const resetQuery = () => { + queryFormRef.value.resetFields(); + handleQuery(); +} +/** 璺宠浆鍒板鐞嗛〉闈� */ +const handleProcess = (row) => { + router.push({ + path: '/workflow/process/detail/' + row.procInsId, + query: { + taskId: row.taskId, + processed: true + } + }) +} + +const categoryFormat = (row) => { + var category = categoryOptions.value.find(function(k) { + return k.code === row.category; + }); + return category && category.categoryName ? category.categoryName : ''; +} + +onMounted(() => { + getCategoryList(); + getList(); +}); +</script> -- Gitblit v1.9.3