添加工作流 设计器
This commit is contained in:
@@ -17,6 +17,7 @@
|
|||||||
"axios": "^1.6.2",
|
"axios": "^1.6.2",
|
||||||
"bpmn-js": "^7.3.1",
|
"bpmn-js": "^7.3.1",
|
||||||
"bpmn-js-properties-panel": "^0.37.2",
|
"bpmn-js-properties-panel": "^0.37.2",
|
||||||
|
"bpmn-js-token-simulation": "0.10.0",
|
||||||
"bpmn-moddle": "^6.0.0",
|
"bpmn-moddle": "^6.0.0",
|
||||||
"camunda-bpmn-moddle": "^4.5.0",
|
"camunda-bpmn-moddle": "^4.5.0",
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
@@ -38,6 +39,7 @@
|
|||||||
"pinia-plugin-persistedstate": "^3.2.1",
|
"pinia-plugin-persistedstate": "^3.2.1",
|
||||||
"screenfull": "^6.0.2",
|
"screenfull": "^6.0.2",
|
||||||
"splitpanes": "^3.1.5",
|
"splitpanes": "^3.1.5",
|
||||||
|
"steady-xml": "0.1.0",
|
||||||
"use-element-plus-theme": "^0.0.5",
|
"use-element-plus-theme": "^0.0.5",
|
||||||
"vue": "^3.3.11",
|
"vue": "^3.3.11",
|
||||||
"vue-baidu-map-3x": "^1.0.35",
|
"vue-baidu-map-3x": "^1.0.35",
|
||||||
|
|||||||
73
pnpm-lock.yaml
generated
73
pnpm-lock.yaml
generated
@@ -29,6 +29,9 @@ dependencies:
|
|||||||
bpmn-js-properties-panel:
|
bpmn-js-properties-panel:
|
||||||
specifier: ^0.37.2
|
specifier: ^0.37.2
|
||||||
version: 0.37.6(bpmn-js@7.5.0)(camunda-bpmn-moddle@4.5.0)
|
version: 0.37.6(bpmn-js@7.5.0)(camunda-bpmn-moddle@4.5.0)
|
||||||
|
bpmn-js-token-simulation:
|
||||||
|
specifier: 0.10.0
|
||||||
|
version: 0.10.0
|
||||||
bpmn-moddle:
|
bpmn-moddle:
|
||||||
specifier: ^6.0.0
|
specifier: ^6.0.0
|
||||||
version: 6.0.7
|
version: 6.0.7
|
||||||
@@ -92,6 +95,9 @@ dependencies:
|
|||||||
splitpanes:
|
splitpanes:
|
||||||
specifier: ^3.1.5
|
specifier: ^3.1.5
|
||||||
version: 3.1.5
|
version: 3.1.5
|
||||||
|
steady-xml:
|
||||||
|
specifier: 0.1.0
|
||||||
|
version: 0.1.0
|
||||||
use-element-plus-theme:
|
use-element-plus-theme:
|
||||||
specifier: ^0.0.5
|
specifier: ^0.0.5
|
||||||
version: 0.0.5(@types/node@20.12.5)(typescript@5.4.4)(vite@5.2.8)
|
version: 0.0.5(@types/node@20.12.5)(typescript@5.4.4)(vite@5.2.8)
|
||||||
@@ -1761,6 +1767,14 @@ packages:
|
|||||||
- camunda-bpmn-moddle
|
- camunda-bpmn-moddle
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/bpmn-js-token-simulation@0.10.0:
|
||||||
|
resolution: {integrity: sha512-QuZQ/KVXKt9Vl+XENyOBoTW2Aw+uKjuBlKdCJL6El7AyM7DkJ5bZkSYURshId1SkBDdYg2mJ1flSmsrhGuSfwg==}
|
||||||
|
dependencies:
|
||||||
|
min-dash: 3.8.1
|
||||||
|
min-dom: 0.2.0
|
||||||
|
svg.js: 2.7.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
/bpmn-js@7.5.0:
|
/bpmn-js@7.5.0:
|
||||||
resolution: {integrity: sha512-0ANaE6Bikg1GmkcvO7RK0MQPX+EKYKBc+q7OWk39/16NcCdNZ/4UiRcCr9n0u1VUCIDsSU/jJ79TIZFnV5CNjw==}
|
resolution: {integrity: sha512-0ANaE6Bikg1GmkcvO7RK0MQPX+EKYKBc+q7OWk39/16NcCdNZ/4UiRcCr9n0u1VUCIDsSU/jJ79TIZFnV5CNjw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -1913,10 +1927,44 @@ packages:
|
|||||||
dev: false
|
dev: false
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
/component-classes@1.2.6:
|
||||||
|
resolution: {integrity: sha512-hPFGULxdwugu1QWW3SvVOCUHLzO34+a2J6Wqy0c5ASQkfi9/8nZcBB0ZohaEbXOQlCflMAEMmEWk7u7BVs4koA==}
|
||||||
|
dependencies:
|
||||||
|
component-indexof: 0.0.3
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/component-closest@0.1.4:
|
||||||
|
resolution: {integrity: sha512-NF9hMj6JKGM5sb6wP/dg7GdJOttaIH9PcTsUNdWcrvu7Kw/5R5swQAFpgaYEHlARrNMyn4Wf7O1PlRej+pt76Q==}
|
||||||
|
dependencies:
|
||||||
|
component-matches-selector: 0.1.7
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/component-delegate@0.2.4:
|
||||||
|
resolution: {integrity: sha512-OlpcB/6Fi+kXQPh/TfXnSvvmrU04ghz7vcJh/jgLF0Ni+I+E3WGlKJQbBGDa5X+kVUG8WxOgjP+8iWbz902fPg==}
|
||||||
|
dependencies:
|
||||||
|
component-closest: 0.1.4
|
||||||
|
component-event: 0.1.4
|
||||||
|
dev: false
|
||||||
|
|
||||||
/component-event@0.1.4:
|
/component-event@0.1.4:
|
||||||
resolution: {integrity: sha512-GMwOG8MnUHP1l8DZx1ztFO0SJTFnIzZnBDkXAj8RM2ntV2A6ALlDxgbMY1Fvxlg6WPQ+5IM/a6vg4PEYbjg/Rw==}
|
resolution: {integrity: sha512-GMwOG8MnUHP1l8DZx1ztFO0SJTFnIzZnBDkXAj8RM2ntV2A6ALlDxgbMY1Fvxlg6WPQ+5IM/a6vg4PEYbjg/Rw==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/component-indexof@0.0.3:
|
||||||
|
resolution: {integrity: sha512-puDQKvx/64HZXb4hBwIcvQLaLgux8o1CbWl39s41hrIIZDl1lJiD5jc22gj3RBeGK0ovxALDYpIbyjqDUUl0rw==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/component-matches-selector@0.1.7:
|
||||||
|
resolution: {integrity: sha512-Yb2+pVBvrqkQVpPaDBF0DYXRreBveXJNrpJs9FnFu8PF6/5IIcz5oDZqiH9nB5hbD2/TmFVN5ZCxBzqu7yFFYQ==}
|
||||||
|
dependencies:
|
||||||
|
component-query: 0.0.3
|
||||||
|
global-object: 1.0.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/component-query@0.0.3:
|
||||||
|
resolution: {integrity: sha512-VgebQseT1hz1Ps7vVp2uaSg+N/gsI5ts3AZUSnN6GMA2M82JH7o+qYifWhmVE/e8w/H48SJuA3nA9uX8zRe95Q==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/compress-commons@4.1.2:
|
/compress-commons@4.1.2:
|
||||||
resolution: {integrity: sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==}
|
resolution: {integrity: sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==}
|
||||||
engines: {node: '>= 10'}
|
engines: {node: '>= 10'}
|
||||||
@@ -2351,6 +2399,10 @@ packages:
|
|||||||
path-is-absolute: 1.0.1
|
path-is-absolute: 1.0.1
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/global-object@1.0.0:
|
||||||
|
resolution: {integrity: sha512-mSPSkY6UsHv6hgW0V2dfWBWTS8TnPnLx3ECVNoWp6rBI2Bg66VYoqGoTFlH/l7XhAZ/l+StYlntXlt87BEeCcg==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/globals@11.12.0:
|
/globals@11.12.0:
|
||||||
resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
|
resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
@@ -2722,6 +2774,18 @@ packages:
|
|||||||
resolution: {integrity: sha512-evumdlmIlg9mbRVPbC4F5FuRhNmcMS5pvuBUbqb1G9v09Ro0ImPEgz5n3khir83lFok1inKqVDjnKEg3GpDxQg==}
|
resolution: {integrity: sha512-evumdlmIlg9mbRVPbC4F5FuRhNmcMS5pvuBUbqb1G9v09Ro0ImPEgz5n3khir83lFok1inKqVDjnKEg3GpDxQg==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/min-dom@0.2.0:
|
||||||
|
resolution: {integrity: sha512-VmxugbnAcVZGqvepjhOA4d4apmrpX8mMaRS+/jo0dI5Yorzrr4Ru9zc9KVALlY/+XakVCb8iQ+PYXljihQcsNw==}
|
||||||
|
dependencies:
|
||||||
|
component-classes: 1.2.6
|
||||||
|
component-closest: 0.1.4
|
||||||
|
component-delegate: 0.2.4
|
||||||
|
component-event: 0.1.4
|
||||||
|
component-matches-selector: 0.1.7
|
||||||
|
component-query: 0.0.3
|
||||||
|
domify: 1.4.2
|
||||||
|
dev: false
|
||||||
|
|
||||||
/min-dom@3.2.1:
|
/min-dom@3.2.1:
|
||||||
resolution: {integrity: sha512-v6YCmnDzxk4rRJntWTUiwggLupPw/8ZSRqUq0PDaBwVZEO/wYzCH4SKVBV+KkEvf3u0XaWHly5JEosPtqRATZA==}
|
resolution: {integrity: sha512-v6YCmnDzxk4rRJntWTUiwggLupPw/8ZSRqUq0PDaBwVZEO/wYzCH4SKVBV+KkEvf3u0XaWHly5JEosPtqRATZA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -3137,6 +3201,11 @@ packages:
|
|||||||
resolution: {integrity: sha512-q+8UfWDg9Itrg0yWK7oe5p/XRCJpJF9OBtXfOPgSJl+u3Xd5KI328RUEvUqSMVM9CiQUEf1QdBzJMkYGErj9QA==}
|
resolution: {integrity: sha512-q+8UfWDg9Itrg0yWK7oe5p/XRCJpJF9OBtXfOPgSJl+u3Xd5KI328RUEvUqSMVM9CiQUEf1QdBzJMkYGErj9QA==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/steady-xml@0.1.0:
|
||||||
|
resolution: {integrity: sha512-5sk17qO2wWRtonTNoBhoKAB35OSsGJOa3+NEa6D+1GS+de+ujDWxnflMkXBrviOfkNrPTUqduAdXhrMJs89nAw==}
|
||||||
|
engines: {node: '>=12.0.0'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/string-argv@0.3.2:
|
/string-argv@0.3.2:
|
||||||
resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==}
|
resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==}
|
||||||
engines: {node: '>=0.6.19'}
|
engines: {node: '>=0.6.19'}
|
||||||
@@ -3182,6 +3251,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==}
|
resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/svg.js@2.7.1:
|
||||||
|
resolution: {integrity: sha512-ycbxpizEQktk3FYvn/8BH+6/EuWXg7ZpQREJvgacqn46gIddG24tNNe4Son6omdXCnSOaApnpZw6MPCBA1dODA==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/tar-stream@2.2.0:
|
/tar-stream@2.2.0:
|
||||||
resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==}
|
resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|||||||
41
src/api/bpm/model.ts
Normal file
41
src/api/bpm/model.ts
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
import createAxios from '@/utils/request'
|
||||||
|
|
||||||
|
//登录获取token
|
||||||
|
export function getRouteMenu() {
|
||||||
|
return createAxios({
|
||||||
|
url: '/user-boot/function/getRouteMenu'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getModelPage = async (params: any) => {
|
||||||
|
return await createAxios({ url: '/bpm/model/page', params, method: 'get' })
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getModel = async (id: number) => {
|
||||||
|
return await createAxios({ url: '/bpm/model/get?id=' + id, method: 'get' })
|
||||||
|
}
|
||||||
|
|
||||||
|
export const updateModel = async (data: any) => {
|
||||||
|
return await createAxios({ url: '/bpm/model/update', method: 'put', data: data })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 任务状态修改
|
||||||
|
export const updateModelState = async (id: number, state: number) => {
|
||||||
|
const data = {
|
||||||
|
id: id,
|
||||||
|
state: state
|
||||||
|
}
|
||||||
|
return await createAxios({ url: '/bpm/model/update-state', method: 'put', data: data })
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createModel = async (data) => {
|
||||||
|
return await createAxios({ url: '/bpm/model/create', method: 'POST', data: data })
|
||||||
|
}
|
||||||
|
|
||||||
|
export const deleteModel = async (id: number) => {
|
||||||
|
return await createAxios({ url: '/bpm/model/delete?id=' + id, method: 'DELETE' })
|
||||||
|
}
|
||||||
|
|
||||||
|
export const deployModel = async (id: number) => {
|
||||||
|
return await createAxios({ url: '/bpm/model/deploy?id=' + id, method: 'POST' })
|
||||||
|
}
|
||||||
66
src/api/process-boot/bpm.ts
Normal file
66
src/api/process-boot/bpm.ts
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import createAxios from '@/utils/request'
|
||||||
|
export type ProcessDefinitionVO = {
|
||||||
|
id: string
|
||||||
|
version: number
|
||||||
|
deploymentTIme: string
|
||||||
|
suspensionState: number
|
||||||
|
}
|
||||||
|
export type ModelVO = {
|
||||||
|
id: number
|
||||||
|
formName: string
|
||||||
|
key: string
|
||||||
|
name: string
|
||||||
|
description: string
|
||||||
|
category: string
|
||||||
|
formType: number
|
||||||
|
formId: number
|
||||||
|
formCustomCreatePath: string
|
||||||
|
formCustomViewPath: string
|
||||||
|
processDefinition: ProcessDefinitionVO
|
||||||
|
status: number
|
||||||
|
remark: string
|
||||||
|
createTime: string
|
||||||
|
bpmnXml: string
|
||||||
|
}
|
||||||
|
//登录获取token
|
||||||
|
export function getRouteMenu() {
|
||||||
|
return createAxios({
|
||||||
|
url: '/user-boot/function/getRouteMenu'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getModelPage = async (params: any) => {
|
||||||
|
return await createAxios({ url: '/bpm/model/page', params, method: 'get' })
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getModel = async (id: number) => {
|
||||||
|
return await createAxios({ url: '/bpm/model/get?id=' + id, method: 'get' })
|
||||||
|
}
|
||||||
|
|
||||||
|
export const updateModel = async (data: any) => {
|
||||||
|
return await createAxios({ url: '/bpm/model/update', method: 'put', data: data })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 任务状态修改
|
||||||
|
export const updateModelState = async (id: number, state: number) => {
|
||||||
|
const data = {
|
||||||
|
id: id,
|
||||||
|
state: state
|
||||||
|
}
|
||||||
|
return await createAxios({ url: '/bpm/model/update-state', method: 'put', data: data })
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createModel = async data => {
|
||||||
|
return await createAxios({ url: '/bpm/model/create', method: 'POST', data: data })
|
||||||
|
}
|
||||||
|
|
||||||
|
export const deleteModel = async (id: number) => {
|
||||||
|
return await createAxios({ url: '/bpm/model/delete?id=' + id, method: 'DELETE' })
|
||||||
|
}
|
||||||
|
|
||||||
|
export const deployModel = async (id: number) => {
|
||||||
|
return await createAxios({ url: '/bpm/model/deploy?id=' + id, method: 'POST' })
|
||||||
|
}
|
||||||
|
export const readXml = async (id: number) => {
|
||||||
|
return await createAxios({ url: 'process-boot/flowable/definition/readXml?deployId=' + id, method: 'GET' })
|
||||||
|
}
|
||||||
@@ -137,9 +137,9 @@ export function addPlanCycle(data: any) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
// 批量提交审核
|
// 批量提交审核
|
||||||
export function planStatus(data: any) {
|
export function submitAuditUser(data: any) {
|
||||||
return createAxios({
|
return createAxios({
|
||||||
url: '/process-boot/rGeneralSurveyPlan/planStatus',
|
url: '/process-boot/rGeneralSurveyPlan/submitAuditUser',
|
||||||
method: 'post',
|
method: 'post',
|
||||||
data
|
data
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -0,0 +1,626 @@
|
|||||||
|
<template>
|
||||||
|
<div class="my-process-designer">
|
||||||
|
<div class="my-process-designer__header" style="z-index: 999; display: table-row-group">
|
||||||
|
<slot name="control-header"></slot>
|
||||||
|
<template v-if="!$slots['control-header']">
|
||||||
|
<ElButtonGroup key="file-control">
|
||||||
|
<el-button icon="el-icon-FolderOpened" @click="refFile.click()">打开文件</el-button>
|
||||||
|
<el-tooltip effect="light" placement="bottom">
|
||||||
|
<template #content>
|
||||||
|
<div style="color: #409eff">
|
||||||
|
<el-button link @click="downloadProcessAsXml()">下载为XML文件</el-button>
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<el-button link @click="downloadProcessAsSvg()">下载为SVG文件</el-button>
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<el-button link @click="downloadProcessAsBpmn()">下载为BPMN文件</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-button icon="el-icon-Download">下载文件</el-button>
|
||||||
|
</el-tooltip>
|
||||||
|
<el-tooltip effect="light">
|
||||||
|
<el-button icon="el-icon-View">浏览</el-button>
|
||||||
|
<template #content>
|
||||||
|
<el-button link @click="previewProcessXML">预览XML</el-button>
|
||||||
|
<br />
|
||||||
|
<el-button link @click="previewProcessJson">预览JSON</el-button>
|
||||||
|
</template>
|
||||||
|
</el-tooltip>
|
||||||
|
<!-- <el-tooltip
|
||||||
|
v-if="props.simulation"
|
||||||
|
effect="light"
|
||||||
|
:content="simulationStatus ? '退出模拟' : '开启模拟'"
|
||||||
|
>
|
||||||
|
<el-button icon="el-icon-Cpu" @click="processSimulation">模拟</el-button>
|
||||||
|
</el-tooltip> -->
|
||||||
|
</ElButtonGroup>
|
||||||
|
<ElButtonGroup key="align-control">
|
||||||
|
<el-tooltip effect="light" content="向左对齐">
|
||||||
|
<el-button icon="el-icon-CaretLeft" @click="elementsAlign('left')" />
|
||||||
|
</el-tooltip>
|
||||||
|
<el-tooltip effect="light" content="向右对齐">
|
||||||
|
<el-button icon="el-icon-CaretRight" @click="elementsAlign('right')" />
|
||||||
|
</el-tooltip>
|
||||||
|
<el-tooltip effect="light" content="向上对齐">
|
||||||
|
<el-button icon="el-icon-CaretTop" @click="elementsAlign('top')" />
|
||||||
|
</el-tooltip>
|
||||||
|
<el-tooltip effect="light" content="向下对齐">
|
||||||
|
<el-button icon="el-icon-CaretBottom" @click="elementsAlign('bottom')" />
|
||||||
|
</el-tooltip>
|
||||||
|
<el-tooltip effect="light" content="水平居中">
|
||||||
|
<el-button icon="el-icon-DCaret" @click="elementsAlign('center')" />
|
||||||
|
</el-tooltip>
|
||||||
|
<el-tooltip effect="light" content="垂直居中">
|
||||||
|
<el-button class="align align-middle" icon="el-icon-DCaret" @click="elementsAlign('middle')" />
|
||||||
|
</el-tooltip>
|
||||||
|
</ElButtonGroup>
|
||||||
|
<ElButtonGroup key="scale-control">
|
||||||
|
<el-tooltip effect="light" content="缩小视图">
|
||||||
|
<el-button :disabled="defaultZoom < 0.2" icon="el-icon-ZoomOut" @click="processZoomOut()" />
|
||||||
|
</el-tooltip>
|
||||||
|
<el-button>{{ Math.floor(defaultZoom * 10 * 10) + '%' }}</el-button>
|
||||||
|
<el-tooltip effect="light" content="放大视图">
|
||||||
|
<el-button :disabled="defaultZoom > 4" icon="el-icon-ZoomIn" @click="processZoomIn()" />
|
||||||
|
</el-tooltip>
|
||||||
|
<el-tooltip effect="light" content="重置视图并居中">
|
||||||
|
<el-button icon="el-icon-ScaleToOriginal" @click="processReZoom()" />
|
||||||
|
</el-tooltip>
|
||||||
|
</ElButtonGroup>
|
||||||
|
<ElButtonGroup key="stack-control">
|
||||||
|
<el-tooltip effect="light" content="撤销">
|
||||||
|
<el-button :disabled="!revocable" icon="el-icon-RefreshLeft" @click="processUndo()" />
|
||||||
|
</el-tooltip>
|
||||||
|
<el-tooltip effect="light" content="恢复">
|
||||||
|
<el-button :disabled="!recoverable" icon="el-icon-RefreshRight" @click="processRedo()" />
|
||||||
|
</el-tooltip>
|
||||||
|
<el-tooltip effect="light" content="重新绘制">
|
||||||
|
<el-button icon="el-icon-Refresh" @click="processRestart" />
|
||||||
|
</el-tooltip>
|
||||||
|
</ElButtonGroup>
|
||||||
|
<el-button
|
||||||
|
icon="el-icon-Plus"
|
||||||
|
title="保存模型"
|
||||||
|
@click="processSave"
|
||||||
|
:type="props.headerButtonType"
|
||||||
|
:disabled="simulationStatus"
|
||||||
|
>
|
||||||
|
保存模型
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
<el-button class="quit" icon="el-icon-ArrowLeftBold" type="primary" @click="emit('quit')">返回</el-button>
|
||||||
|
<!-- 用于打开本地文件-->
|
||||||
|
<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="bpmnCanvas"
|
||||||
|
id="bpmnCanvas"
|
||||||
|
:style="`width: 100%; height: ${height}`"
|
||||||
|
></div>
|
||||||
|
<!-- <div id="js-properties-panel" class="panel"></div> -->
|
||||||
|
<!-- <div class="my-process-designer__canvas" ref="bpmn-canvas"></div> -->
|
||||||
|
</div>
|
||||||
|
<!-- <Dialog title="预览" v-model="previewModelVisible" width="80%" :scroll="true" max-height="600px">
|
||||||
|
<div v-highlight>
|
||||||
|
<code class="hljs">
|
||||||
|
{{ previewResult }}
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
</Dialog> -->
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
// import 'bpmn-js/dist/assets/diagram-js.css' // 左边工具栏以及编辑节点的样式
|
||||||
|
// import 'bpmn-js/dist/assets/bpmn-font/css/bpmn.css'
|
||||||
|
// import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css'
|
||||||
|
// import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css'
|
||||||
|
// import 'bpmn-js-properties-panel/dist/assets/bpmn-js-properties-panel.css' // 右侧框样式
|
||||||
|
import { onMounted, provide, ref, computed, onBeforeUnmount, onBeforeMount } from 'vue'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import BpmnModeler from 'bpmn-js/lib/Modeler'
|
||||||
|
import DefaultEmptyXML from './plugins/defaultEmpty'
|
||||||
|
import { mainHeight } from '@/utils/layout'
|
||||||
|
// 翻译方法
|
||||||
|
import customTranslate from './plugins/translate/customTranslate'
|
||||||
|
import translationsCN from './plugins/translate/zh'
|
||||||
|
// 模拟流转流程
|
||||||
|
import tokenSimulation from 'bpmn-js-token-simulation'
|
||||||
|
// 标签解析构建器
|
||||||
|
// import bpmnPropertiesProvider from "bpmn-js-properties-panel/lib/provider/bpmn";
|
||||||
|
// import propertiesPanelModule from 'bpmn-js-properties-panel'
|
||||||
|
// import propertiesProviderModule from 'bpmn-js-properties-panel/lib/provider/camunda'
|
||||||
|
// 标签解析 Moddle
|
||||||
|
import camundaModdleDescriptor from './plugins/descriptor/camundaDescriptor.json'
|
||||||
|
import activitiModdleDescriptor from './plugins/descriptor/activitiDescriptor.json'
|
||||||
|
import flowableModdleDescriptor from './plugins/descriptor/flowableDescriptor.json'
|
||||||
|
// 标签解析 Extension
|
||||||
|
import camundaModdleExtension from './plugins/extension-moddle/camunda'
|
||||||
|
import activitiModdleExtension from './plugins/extension-moddle/activiti'
|
||||||
|
import flowableModdleExtension from './plugins/extension-moddle/flowable'
|
||||||
|
// 引入json转换与高亮
|
||||||
|
// import xml2js from 'xml-js'
|
||||||
|
// import xml2js from 'fast-xml-parser'
|
||||||
|
import { XmlNode, XmlNodeType, parseXmlString } from 'steady-xml'
|
||||||
|
// 代码高亮插件
|
||||||
|
// import hljs from 'highlight.js/lib/highlight'
|
||||||
|
// import 'highlight.js/styles/github-gist.css'
|
||||||
|
// hljs.registerLanguage('xml', 'highlight.js/lib/languages/xml')
|
||||||
|
// hljs.registerLanguage('json', 'highlight.js/lib/languages/json')
|
||||||
|
// const eventName = reactive({
|
||||||
|
// name: ''
|
||||||
|
// })
|
||||||
|
|
||||||
|
defineOptions({ name: 'MyProcessDesigner' })
|
||||||
|
|
||||||
|
const bpmnCanvas = ref()
|
||||||
|
const refFile = ref()
|
||||||
|
const emit = defineEmits([
|
||||||
|
'destroy',
|
||||||
|
'init-finished',
|
||||||
|
'save',
|
||||||
|
'commandStack-changed',
|
||||||
|
'input',
|
||||||
|
'change',
|
||||||
|
'canvas-viewbox-changed',
|
||||||
|
// eventName.name
|
||||||
|
'element-click',
|
||||||
|
'quit'
|
||||||
|
])
|
||||||
|
const height = mainHeight(60).height
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
value: String, // xml 字符串
|
||||||
|
// valueWatch: true, // xml 字符串的 watch 状态
|
||||||
|
processId: String, // 流程 key 标识
|
||||||
|
processName: String, // 流程 name 名字
|
||||||
|
formId: Number, // 流程 form 表单编号
|
||||||
|
translations: {
|
||||||
|
// 自定义的翻译文件
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
},
|
||||||
|
additionalModel: [Object, Array], // 自定义model
|
||||||
|
moddleExtension: {
|
||||||
|
// 自定义moddle
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
},
|
||||||
|
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: string) => ['default', 'medium', 'small', 'mini'].indexOf(value) !== -1
|
||||||
|
},
|
||||||
|
headerButtonType: {
|
||||||
|
type: String,
|
||||||
|
default: 'primary',
|
||||||
|
validator: (value: string) =>
|
||||||
|
['default', 'primary', 'success', 'warning', 'danger', 'info'].indexOf(value) !== -1
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
provide('configGlobal', props)
|
||||||
|
let bpmnModeler: any = null
|
||||||
|
const defaultZoom = ref(1)
|
||||||
|
const previewModelVisible = ref(false)
|
||||||
|
const simulationStatus = ref(false)
|
||||||
|
const previewResult = ref('')
|
||||||
|
const previewType = ref('xml')
|
||||||
|
const recoverable = ref(false)
|
||||||
|
const revocable = ref(false)
|
||||||
|
const additionalModules = computed(() => {
|
||||||
|
console.log(props.additionalModel, 'additionalModel')
|
||||||
|
const Modules: any[] = []
|
||||||
|
// 仅保留用户自定义扩展模块
|
||||||
|
if (props.onlyCustomizeAddi) {
|
||||||
|
if (Object.prototype.toString.call(props.additionalModel) == '[object Array]') {
|
||||||
|
return props.additionalModel || []
|
||||||
|
}
|
||||||
|
return [props.additionalModel]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 插入用户自定义扩展模块
|
||||||
|
if (Object.prototype.toString.call(props.additionalModel) == '[object Array]') {
|
||||||
|
Modules.push(...(props.additionalModel as any[]))
|
||||||
|
} else {
|
||||||
|
props.additionalModel && Modules.push(props.additionalModel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 翻译模块
|
||||||
|
const TranslateModule = {
|
||||||
|
translate: ['value', customTranslate(props.translations || translationsCN)]
|
||||||
|
}
|
||||||
|
Modules.push(TranslateModule)
|
||||||
|
|
||||||
|
// 模拟流转模块
|
||||||
|
if (props.simulation) {
|
||||||
|
Modules.push(tokenSimulation)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据需要的流程类型设置扩展元素构建模块
|
||||||
|
// if (this.prefix === "bpmn") {
|
||||||
|
// Modules.push(bpmnModdleExtension);
|
||||||
|
// }
|
||||||
|
console.log(props.prefix, 'props.prefix ')
|
||||||
|
if (props.prefix === 'camunda') {
|
||||||
|
Modules.push(camundaModdleExtension)
|
||||||
|
}
|
||||||
|
if (props.prefix === 'flowable') {
|
||||||
|
Modules.push(flowableModdleExtension)
|
||||||
|
}
|
||||||
|
if (props.prefix === 'activiti') {
|
||||||
|
Modules.push(activitiModdleExtension)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Modules
|
||||||
|
})
|
||||||
|
const moddleExtensions = computed(() => {
|
||||||
|
console.log(props.onlyCustomizeModdle, 'props.onlyCustomizeModdle')
|
||||||
|
console.log(props.moddleExtension, 'props.moddleExtension')
|
||||||
|
console.log(props.prefix, 'props.prefix')
|
||||||
|
const Extensions: any = {}
|
||||||
|
// 仅使用用户自定义模块
|
||||||
|
if (props.onlyCustomizeModdle) {
|
||||||
|
return props.moddleExtension || null
|
||||||
|
}
|
||||||
|
|
||||||
|
// 插入用户自定义模块
|
||||||
|
if (props.moddleExtension) {
|
||||||
|
for (let key in props.moddleExtension) {
|
||||||
|
Extensions[key] = props.moddleExtension[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据需要的 "流程类型" 设置 对应的解析文件
|
||||||
|
if (props.prefix === 'activiti') {
|
||||||
|
Extensions.activiti = activitiModdleDescriptor
|
||||||
|
}
|
||||||
|
if (props.prefix === 'flowable') {
|
||||||
|
Extensions.flowable = flowableModdleDescriptor
|
||||||
|
}
|
||||||
|
if (props.prefix === 'camunda') {
|
||||||
|
Extensions.camunda = camundaModdleDescriptor
|
||||||
|
}
|
||||||
|
return Extensions
|
||||||
|
})
|
||||||
|
console.log(additionalModules, 'additionalModules()')
|
||||||
|
console.log(moddleExtensions, 'moddleExtensions()')
|
||||||
|
const initBpmnModeler = () => {
|
||||||
|
if (bpmnModeler) return
|
||||||
|
let data = document.getElementById('bpmnCanvas')
|
||||||
|
console.log(data, 'data')
|
||||||
|
console.log(props.keyboard, 'props.keyboard')
|
||||||
|
console.log(additionalModules, 'additionalModules()')
|
||||||
|
console.log(moddleExtensions, 'moddleExtensions()')
|
||||||
|
|
||||||
|
bpmnModeler = new BpmnModeler({
|
||||||
|
// container: this.$refs['bpmn-canvas'],
|
||||||
|
// container: getCurrentInstance(),
|
||||||
|
// container: needClass,
|
||||||
|
// container: bpmnCanvas.value,
|
||||||
|
container: data,
|
||||||
|
// width: '100%',
|
||||||
|
// 添加控制板
|
||||||
|
// propertiesPanel: {
|
||||||
|
// parent: '#js-properties-panel'
|
||||||
|
// },
|
||||||
|
keyboard: props.keyboard ? { bindTo: document } : null,
|
||||||
|
// additionalModules: additionalModules.value,
|
||||||
|
additionalModules: additionalModules.value,
|
||||||
|
moddleExtensions: moddleExtensions.value
|
||||||
|
|
||||||
|
// additionalModules: [
|
||||||
|
// additionalModules.value
|
||||||
|
// propertiesPanelModule,
|
||||||
|
// propertiesProviderModule
|
||||||
|
// propertiesProviderModule
|
||||||
|
// ],
|
||||||
|
// moddleExtensions: { camunda: moddleExtensions.value }
|
||||||
|
})
|
||||||
|
|
||||||
|
// bpmnModeler.createDiagram()
|
||||||
|
|
||||||
|
// console.log(bpmnModeler, 'bpmnModeler111111')
|
||||||
|
emit('init-finished', bpmnModeler)
|
||||||
|
initModelListeners()
|
||||||
|
}
|
||||||
|
|
||||||
|
const initModelListeners = () => {
|
||||||
|
const EventBus = bpmnModeler.get('eventBus')
|
||||||
|
console.log(EventBus, 'EventBus')
|
||||||
|
// 注册需要的监听事件, 将. 替换为 - , 避免解析异常
|
||||||
|
props.events.forEach((event: any) => {
|
||||||
|
EventBus.on(event, function (eventObj) {
|
||||||
|
let eventName = event.replace(/\./g, '-')
|
||||||
|
// eventName.name = eventName
|
||||||
|
let element = eventObj ? eventObj.element : null
|
||||||
|
console.log(eventName, 'eventName')
|
||||||
|
console.log(element, 'element')
|
||||||
|
emit('element-click', element, eventObj)
|
||||||
|
// emit(eventName, element, eventObj)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
// 监听图形改变返回xml
|
||||||
|
EventBus.on('commandStack.changed', async event => {
|
||||||
|
try {
|
||||||
|
recoverable.value = bpmnModeler.get('commandStack').canRedo()
|
||||||
|
revocable.value = bpmnModeler.get('commandStack').canUndo()
|
||||||
|
let { xml } = await bpmnModeler.saveXML({ format: true })
|
||||||
|
emit('commandStack-changed', event)
|
||||||
|
emit('input', xml)
|
||||||
|
emit('change', xml)
|
||||||
|
} catch (e: any) {
|
||||||
|
console.error(`[Process Designer Warn]: ${e.message || e}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// 监听视图缩放变化
|
||||||
|
bpmnModeler.on('canvas.viewbox.changed', ({ viewbox }) => {
|
||||||
|
emit('canvas-viewbox-changed', { viewbox })
|
||||||
|
const { scale } = viewbox
|
||||||
|
defaultZoom.value = Math.floor(scale * 100) / 100
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/* 创建新的流程图 */
|
||||||
|
const createNewDiagram = async xml => {
|
||||||
|
console.log(xml, 'xml')
|
||||||
|
// 将字符串转换成图显示出来
|
||||||
|
let newId = props.processId || `Process_${new Date().getTime()}`
|
||||||
|
let newName = props.processName || `业务流程_${new Date().getTime()}`
|
||||||
|
let xmlString = xml || DefaultEmptyXML(newId, newName, props.prefix)
|
||||||
|
try {
|
||||||
|
// console.log(xmlString, 'xmlString')
|
||||||
|
// console.log(this.bpmnModeler.importXML);
|
||||||
|
let { warnings } = await bpmnModeler.importXML(xmlString)
|
||||||
|
console.log(warnings, 'warnings')
|
||||||
|
if (warnings && warnings.length) {
|
||||||
|
warnings.forEach(warn => console.warn(warn))
|
||||||
|
}
|
||||||
|
} catch (e: any) {
|
||||||
|
console.error(`[Process Designer Warn]: ${e.message || e}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下载流程图到本地
|
||||||
|
const downloadProcess = async type => {
|
||||||
|
try {
|
||||||
|
// 按需要类型创建文件并下载
|
||||||
|
if (type === 'xml' || type === 'bpmn') {
|
||||||
|
const { err, xml } = await bpmnModeler.saveXML()
|
||||||
|
// 读取异常时抛出异常
|
||||||
|
if (err) {
|
||||||
|
console.error(`[Process Designer Warn ]: ${err.message || err}`)
|
||||||
|
}
|
||||||
|
let { href, filename } = setEncoded(type.toUpperCase(), xml)
|
||||||
|
downloadFunc(href, filename)
|
||||||
|
} else {
|
||||||
|
const { err, svg } = await bpmnModeler.saveSVG()
|
||||||
|
// 读取异常时抛出异常
|
||||||
|
if (err) {
|
||||||
|
return console.error(err)
|
||||||
|
}
|
||||||
|
let { href, filename } = setEncoded('SVG', svg)
|
||||||
|
downloadFunc(href, filename)
|
||||||
|
}
|
||||||
|
} catch (e: any) {
|
||||||
|
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 对象
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据所需类型进行转码并返回下载地址
|
||||||
|
const setEncoded = (type, data) => {
|
||||||
|
const filename = 'diagram'
|
||||||
|
const encodedData = encodeURIComponent(data)
|
||||||
|
return {
|
||||||
|
filename: `${filename}.${type}`,
|
||||||
|
href: `data:application/${type === 'svg' ? 'text/xml' : 'bpmn20-xml'};charset=UTF-8,${encodedData}`,
|
||||||
|
data: data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加载本地文件
|
||||||
|
const importLocalFile = () => {
|
||||||
|
const file = refFile.value.files[0]
|
||||||
|
const reader = new FileReader()
|
||||||
|
reader.readAsText(file)
|
||||||
|
reader.onload = function () {
|
||||||
|
let xmlStr = this.result
|
||||||
|
createNewDiagram(xmlStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* ------------------------------------------------ refs methods ------------------------------------------------------ */
|
||||||
|
const downloadProcessAsXml = () => {
|
||||||
|
downloadProcess('xml')
|
||||||
|
}
|
||||||
|
const downloadProcessAsBpmn = () => {
|
||||||
|
downloadProcess('bpmn')
|
||||||
|
}
|
||||||
|
const downloadProcessAsSvg = () => {
|
||||||
|
downloadProcess('svg')
|
||||||
|
}
|
||||||
|
const processSimulation = () => {
|
||||||
|
simulationStatus.value = !simulationStatus.value
|
||||||
|
console.log(bpmnModeler.get('toggleMode', 'strict'), "bpmnModeler.get('toggleMode')")
|
||||||
|
props.simulation && bpmnModeler.get('toggleMode', 'strict').toggleMode()
|
||||||
|
}
|
||||||
|
const processRedo = () => {
|
||||||
|
bpmnModeler.get('commandStack').redo()
|
||||||
|
}
|
||||||
|
const processUndo = () => {
|
||||||
|
bpmnModeler.get('commandStack').undo()
|
||||||
|
}
|
||||||
|
const processZoomIn = (zoomStep = 0.1) => {
|
||||||
|
let newZoom = Math.floor(defaultZoom.value * 100 + zoomStep * 100) / 100
|
||||||
|
if (newZoom > 4) {
|
||||||
|
throw new Error('[Process Designer Warn ]: The zoom ratio cannot be greater than 4')
|
||||||
|
}
|
||||||
|
defaultZoom.value = newZoom
|
||||||
|
bpmnModeler.get('canvas').zoom(defaultZoom.value)
|
||||||
|
}
|
||||||
|
const processZoomOut = (zoomStep = 0.1) => {
|
||||||
|
let newZoom = Math.floor(defaultZoom.value * 100 - zoomStep * 100) / 100
|
||||||
|
if (newZoom < 0.2) {
|
||||||
|
throw new Error('[Process Designer Warn ]: The zoom ratio cannot be less than 0.2')
|
||||||
|
}
|
||||||
|
defaultZoom.value = newZoom
|
||||||
|
bpmnModeler.get('canvas').zoom(defaultZoom.value)
|
||||||
|
}
|
||||||
|
// const 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')
|
||||||
|
// }
|
||||||
|
// defaultZoom = newZoom
|
||||||
|
// bpmnModeler.get('canvas').zoom(newZoom)
|
||||||
|
// }
|
||||||
|
const processReZoom = () => {
|
||||||
|
defaultZoom.value = 1
|
||||||
|
bpmnModeler.get('canvas').zoom('fit-viewport', 'auto')
|
||||||
|
}
|
||||||
|
const processRestart = () => {
|
||||||
|
recoverable.value = false
|
||||||
|
revocable.value = false
|
||||||
|
createNewDiagram(null)
|
||||||
|
}
|
||||||
|
const elementsAlign = align => {
|
||||||
|
const Align = bpmnModeler.get('alignElements')
|
||||||
|
const Selection = bpmnModeler.get('selection')
|
||||||
|
const SelectedElements = Selection.get()
|
||||||
|
if (!SelectedElements || SelectedElements.length <= 1) {
|
||||||
|
ElMessage.warning('请按住 Shift 键选择多个元素对齐')
|
||||||
|
// alert('请按住 Ctrl 键选择多个元素对齐
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ElMessageBox.confirm('自动对齐可能造成图形变形,是否继续?', '警告', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
Align.trigger(SelectedElements, align)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/*----------------------------- 方法结束 ---------------------------------*/
|
||||||
|
const previewProcessXML = () => {
|
||||||
|
console.log(bpmnModeler.saveXML, 'bpmnModeler')
|
||||||
|
bpmnModeler.saveXML({ format: true }).then(({ xml }) => {
|
||||||
|
// console.log(xml, 'xml111111')
|
||||||
|
previewResult.value = xml
|
||||||
|
previewType.value = 'xml'
|
||||||
|
previewModelVisible.value = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const previewProcessJson = () => {
|
||||||
|
bpmnModeler.saveXML({ format: true }).then(({ xml }) => {
|
||||||
|
// console.log(xml, 'xml')
|
||||||
|
|
||||||
|
// const rootNode = parseXmlString(xml)
|
||||||
|
// console.log(rootNode, 'rootNoderootNode')
|
||||||
|
const rootNodes = new XmlNode(XmlNodeType.Root, parseXmlString(xml))
|
||||||
|
// console.log(rootNodes, 'rootNodesrootNodesrootNodes')
|
||||||
|
// console.log(rootNodes.parent.toJsObject(), 'rootNodes.toJSON()')
|
||||||
|
// console.log(JSON.stringify(rootNodes.parent.toJsObject()), 'rootNodes.toJSON()')
|
||||||
|
// console.log(JSON.stringify(rootNodes.parent.toJSON()), 'rootNodes.toJSON()')
|
||||||
|
|
||||||
|
// const parser = new xml2js.XMLParser()
|
||||||
|
// let jObj = parser.parse(xml)
|
||||||
|
// console.log(jObj, 'jObjjObjjObjjObjjObj')
|
||||||
|
// const builder = new xml2js.XMLBuilder(xml)
|
||||||
|
// const xmlContent = builder
|
||||||
|
// console.log(xmlContent, 'xmlContent')
|
||||||
|
// console.log(xml2js, 'convertconvertconvert')
|
||||||
|
previewResult.value = rootNodes.parent?.toJSON() as unknown as string
|
||||||
|
// previewResult.value = jObj
|
||||||
|
// previewResult.value = convert.xml2json(xml, {explicitArray : false},{ spaces: 2 })
|
||||||
|
previewType.value = 'json'
|
||||||
|
previewModelVisible.value = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/* ------------------------------------------------ 芋道源码 methods ------------------------------------------------------ */
|
||||||
|
const processSave = async () => {
|
||||||
|
// console.log(bpmnModeler, 'bpmnModelerbpmnModelerbpmnModelerbpmnModeler')
|
||||||
|
const { err, xml } = await bpmnModeler.saveXML()
|
||||||
|
// console.log(err, 'errerrerrerrerr')
|
||||||
|
// console.log(xml, 'xmlxmlxmlxmlxml')
|
||||||
|
// 读取异常时抛出异常
|
||||||
|
if (err) {
|
||||||
|
// this.$modal.msgError('保存模型失败,请重试!')
|
||||||
|
alert('保存模型失败,请重试!')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 触发 save 事件
|
||||||
|
emit('save', xml)
|
||||||
|
}
|
||||||
|
/** 高亮显示 */
|
||||||
|
// const highlightedCode = (previewType, previewResult) => {
|
||||||
|
// console.log(previewType, 'previewType, previewResult')
|
||||||
|
// console.log(previewResult, 'previewType, previewResult')
|
||||||
|
// console.log(hljs.highlight, 'hljs.highlight')
|
||||||
|
// const result = hljs.highlight(previewType, previewResult.value || '', true)
|
||||||
|
// return result.value || ' '
|
||||||
|
// }
|
||||||
|
onBeforeMount(() => {
|
||||||
|
console.log(props, 'propspropspropsprops')
|
||||||
|
})
|
||||||
|
onMounted(() => {
|
||||||
|
initBpmnModeler()
|
||||||
|
createNewDiagram(props.value)
|
||||||
|
})
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
// this.$once('hook:beforeDestroy', () => {
|
||||||
|
// })
|
||||||
|
if (bpmnModeler) bpmnModeler.destroy()
|
||||||
|
emit('destroy', bpmnModeler)
|
||||||
|
bpmnModeler = null
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.quit {
|
||||||
|
position: absolute;
|
||||||
|
top: 5px;
|
||||||
|
right: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,664 @@
|
|||||||
|
<template>
|
||||||
|
<div class="my-process-designer">
|
||||||
|
<div class="my-process-designer__container">
|
||||||
|
<div class="my-process-designer__canvas" style="height: 760px" ref="bpmnCanvas"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import BpmnViewer from 'bpmn-js/lib/Viewer'
|
||||||
|
import DefaultEmptyXML from './plugins/defaultEmpty'
|
||||||
|
// import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||||
|
// import { formatDate } from '@/utils/formatTime'
|
||||||
|
// import { isEmpty } from '@/utils/is'
|
||||||
|
|
||||||
|
defineOptions({ name: 'MyProcessViewer' })
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
value: {
|
||||||
|
// BPMN XML 字符串
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
prefix: {
|
||||||
|
// 使用哪个引擎
|
||||||
|
type: String,
|
||||||
|
default: 'camunda'
|
||||||
|
},
|
||||||
|
activityData: {
|
||||||
|
// 活动的数据。传递时,可高亮流程
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
processInstanceData: {
|
||||||
|
// 流程实例的数据。传递时,可展示流程发起人等信息
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
},
|
||||||
|
taskData: {
|
||||||
|
// 任务实例的数据。传递时,可展示 UserTask 审核相关的信息
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
provide('configGlobal', props)
|
||||||
|
|
||||||
|
const emit = defineEmits(['destroy'])
|
||||||
|
|
||||||
|
let bpmnModeler
|
||||||
|
|
||||||
|
const xml = ref('')
|
||||||
|
const activityLists = ref<any[]>([])
|
||||||
|
const processInstance = ref<any>(undefined)
|
||||||
|
const taskList = ref<any[]>([])
|
||||||
|
const bpmnCanvas = ref()
|
||||||
|
// const element = ref()
|
||||||
|
const elementOverlayIds = ref<any>(null)
|
||||||
|
const overlays = ref<any>(null)
|
||||||
|
|
||||||
|
const initBpmnModeler = () => {
|
||||||
|
if (bpmnModeler) return
|
||||||
|
bpmnModeler = new BpmnViewer({
|
||||||
|
container: bpmnCanvas.value,
|
||||||
|
bpmnRenderer: {}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 创建新的流程图 */
|
||||||
|
const createNewDiagram = async (xml) => {
|
||||||
|
// 将字符串转换成图显示出来
|
||||||
|
let newId = `Process_${new Date().getTime()}`
|
||||||
|
let newName = `业务流程_${new Date().getTime()}`
|
||||||
|
let xmlString = xml || DefaultEmptyXML(newId, newName, props.prefix)
|
||||||
|
try {
|
||||||
|
let { warnings } = await bpmnModeler.importXML(xmlString)
|
||||||
|
if (warnings && warnings.length) {
|
||||||
|
warnings.forEach((warn) => console.warn(warn))
|
||||||
|
}
|
||||||
|
// 高亮流程图
|
||||||
|
await highlightDiagram()
|
||||||
|
const canvas = bpmnModeler.get('canvas')
|
||||||
|
canvas.zoom('fit-viewport', 'auto')
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
// console.error(`[Process Designer Warn]: ${e?.message || e}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 高亮流程图 */
|
||||||
|
// TODO 芋艿:如果多个 endActivity 的话,目前的逻辑可能有一定的问题。https://www.jdon.com/workflow/multi-events.html
|
||||||
|
const highlightDiagram = async () => {
|
||||||
|
const activityList = activityLists.value
|
||||||
|
if (activityList.length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 参考自 https://gitee.com/tony2y/RuoYi-flowable/blob/master/ruoyi-ui/src/components/Process/index.vue#L222 实现
|
||||||
|
// 再次基础上,增加不同审批结果的颜色等等
|
||||||
|
let canvas = bpmnModeler.get('canvas')
|
||||||
|
let todoActivity: any = activityList.find((m: any) => !m.endTime) // 找到待办的任务
|
||||||
|
let endActivity: any = activityList[activityList.length - 1] // 获得最后一个任务
|
||||||
|
let findProcessTask = false //是否已经高亮了进行中的任务
|
||||||
|
//进行中高亮之后的任务 key 集合,用于过滤掉 taskList 进行中后面的任务,避免进行中后面的数据 Hover 还有数据
|
||||||
|
let removeTaskDefinitionKeyList = []
|
||||||
|
// debugger
|
||||||
|
bpmnModeler.getDefinitions().rootElements[0].flowElements?.forEach((n: any) => {
|
||||||
|
let activity: any = activityList.find((m: any) => m.key === n.id) // 找到对应的活动
|
||||||
|
if (!activity) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (n.$type === 'bpmn:UserTask') {
|
||||||
|
// 用户任务
|
||||||
|
// 处理用户任务的高亮
|
||||||
|
const task: any = taskList.value.find((m: any) => m.id === activity.taskId) // 找到活动对应的 taskId
|
||||||
|
if (!task) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 进行中的任务已经高亮过了,则不高亮后面的任务了
|
||||||
|
if (findProcessTask) {
|
||||||
|
removeTaskDefinitionKeyList.push(n.id)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 高亮任务
|
||||||
|
canvas.addMarker(n.id, getResultCss(task.status))
|
||||||
|
//标记是否高亮了进行中任务
|
||||||
|
if (task.status === 1) {
|
||||||
|
findProcessTask = true
|
||||||
|
}
|
||||||
|
// 如果非通过,就不走后面的线条了
|
||||||
|
if (task.status !== 2) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 处理 outgoing 出线
|
||||||
|
const outgoing = getActivityOutgoing(activity)
|
||||||
|
outgoing?.forEach((nn: any) => {
|
||||||
|
// debugger
|
||||||
|
let targetActivity: any = activityList.find((m: any) => m.key === nn.targetRef.id)
|
||||||
|
// 如果目标活动存在,则根据该活动是否结束,进行【bpmn:SequenceFlow】连线的高亮设置
|
||||||
|
if (targetActivity) {
|
||||||
|
canvas.addMarker(nn.id, targetActivity.endTime ? 'highlight' : 'highlight-todo')
|
||||||
|
} else if (nn.targetRef.$type === 'bpmn:ExclusiveGateway') {
|
||||||
|
// TODO 芋艿:这个流程,暂时没走到过
|
||||||
|
canvas.addMarker(nn.id, activity.endTime ? 'highlight' : 'highlight-todo')
|
||||||
|
canvas.addMarker(nn.targetRef.id, activity.endTime ? 'highlight' : 'highlight-todo')
|
||||||
|
} else if (nn.targetRef.$type === 'bpmn:EndEvent') {
|
||||||
|
// TODO 芋艿:这个流程,暂时没走到过
|
||||||
|
if (!todoActivity && endActivity.key === n.id) {
|
||||||
|
canvas.addMarker(nn.id, 'highlight')
|
||||||
|
canvas.addMarker(nn.targetRef.id, 'highlight')
|
||||||
|
}
|
||||||
|
if (!activity.endTime) {
|
||||||
|
canvas.addMarker(nn.id, 'highlight-todo')
|
||||||
|
canvas.addMarker(nn.targetRef.id, 'highlight-todo')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else if (n.$type === 'bpmn:ExclusiveGateway') {
|
||||||
|
// 排它网关
|
||||||
|
// 设置【bpmn:ExclusiveGateway】排它网关的高亮
|
||||||
|
canvas.addMarker(n.id, getActivityHighlightCss(activity))
|
||||||
|
// 查找需要高亮的连线
|
||||||
|
let matchNN: any = undefined
|
||||||
|
let matchActivity: any = undefined
|
||||||
|
n.outgoing?.forEach((nn: any) => {
|
||||||
|
let targetActivity = activityList.find((m: any) => m.key === nn.targetRef.id)
|
||||||
|
if (!targetActivity) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 特殊判断 endEvent 类型的原因,ExclusiveGateway 可能后续连有 2 个路径:
|
||||||
|
// 1. 一个是 UserTask => EndEvent
|
||||||
|
// 2. 一个是 EndEvent
|
||||||
|
// 在选择路径 1 时,其实 EndEvent 可能也存在,导致 1 和 2 都高亮,显然是不正确的。
|
||||||
|
// 所以,在 matchActivity 为 EndEvent 时,需要进行覆盖~~
|
||||||
|
if (!matchActivity || matchActivity.type === 'endEvent') {
|
||||||
|
matchNN = nn
|
||||||
|
matchActivity = targetActivity
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (matchNN && matchActivity) {
|
||||||
|
canvas.addMarker(matchNN.id, getActivityHighlightCss(matchActivity))
|
||||||
|
}
|
||||||
|
} else if (n.$type === 'bpmn:ParallelGateway') {
|
||||||
|
// 并行网关
|
||||||
|
// 设置【bpmn:ParallelGateway】并行网关的高亮
|
||||||
|
canvas.addMarker(n.id, getActivityHighlightCss(activity))
|
||||||
|
n.outgoing?.forEach((nn: any) => {
|
||||||
|
// 获得连线是否有指向目标。如果有,则进行高亮
|
||||||
|
const targetActivity = activityList.find((m: any) => m.key === nn.targetRef.id)
|
||||||
|
if (targetActivity) {
|
||||||
|
canvas.addMarker(nn.id, getActivityHighlightCss(targetActivity)) // 高亮【bpmn:SequenceFlow】连线
|
||||||
|
// 高亮【...】目标。其中 ... 可以是 bpm:UserTask、也可以是其它的。当然,如果是 bpm:UserTask 的话,其实不做高亮也没问题,因为上面有逻辑做了这块。
|
||||||
|
canvas.addMarker(nn.targetRef.id, getActivityHighlightCss(targetActivity))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else if (n.$type === 'bpmn:StartEvent') {
|
||||||
|
// 开始节点
|
||||||
|
canvas.addMarker(n.id, 'highlight')
|
||||||
|
n.outgoing?.forEach((nn) => {
|
||||||
|
// outgoing 例如说【bpmn:SequenceFlow】连线
|
||||||
|
// 获得连线是否有指向目标。如果有,则进行高亮
|
||||||
|
let targetActivity = activityList.find((m: any) => m.key === nn.targetRef.id)
|
||||||
|
if (targetActivity) {
|
||||||
|
canvas.addMarker(nn.id, 'highlight') // 高亮【bpmn:SequenceFlow】连线
|
||||||
|
canvas.addMarker(n.id, 'highlight') // 高亮【bpmn:StartEvent】开始节点(自己)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else if (n.$type === 'bpmn:EndEvent') {
|
||||||
|
// 结束节点
|
||||||
|
if (!processInstance.value || processInstance.value.status === 1) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
canvas.addMarker(n.id, getResultCss(processInstance.value.status))
|
||||||
|
} else if (n.$type === 'bpmn:ServiceTask') {
|
||||||
|
//服务任务
|
||||||
|
if (activity.startTime > 0 && activity.endTime === 0) {
|
||||||
|
//进入执行,标识进行色
|
||||||
|
canvas.addMarker(n.id, getResultCss(1))
|
||||||
|
}
|
||||||
|
if (activity.endTime > 0) {
|
||||||
|
// 执行完成,节点标识完成色, 所有outgoing标识完成色。
|
||||||
|
canvas.addMarker(n.id, getResultCss(2))
|
||||||
|
const outgoing = getActivityOutgoing(activity)
|
||||||
|
outgoing?.forEach((out) => {
|
||||||
|
canvas.addMarker(out.id, getResultCss(2))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else if (n.$type === 'bpmn:SequenceFlow') {
|
||||||
|
let targetActivity = activityList.find((m: any) => m.key === n.targetRef.id)
|
||||||
|
if (targetActivity) {
|
||||||
|
canvas.addMarker(n.id, getActivityHighlightCss(targetActivity))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// if (!isEmpty(removeTaskDefinitionKeyList)) {
|
||||||
|
// taskList.value = taskList.value.filter(
|
||||||
|
// (item) => !removeTaskDefinitionKeyList.includes(item.taskDefinitionKey)
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
const getActivityHighlightCss = (activity) => {
|
||||||
|
return activity.endTime ? 'highlight' : 'highlight-todo'
|
||||||
|
}
|
||||||
|
|
||||||
|
const getResultCss = (status) => {
|
||||||
|
if (status === 1) {
|
||||||
|
// 审批中
|
||||||
|
return 'highlight-todo'
|
||||||
|
} else if (status === 2) {
|
||||||
|
// 已通过
|
||||||
|
return 'highlight'
|
||||||
|
} else if (status === 3) {
|
||||||
|
// 不通过
|
||||||
|
return 'highlight-reject'
|
||||||
|
} else if (status === 4) {
|
||||||
|
// 已取消
|
||||||
|
return 'highlight-cancel'
|
||||||
|
} else if (status === 5) {
|
||||||
|
// 退回
|
||||||
|
return 'highlight-return'
|
||||||
|
} else if (status === 6) {
|
||||||
|
// 委派
|
||||||
|
return 'highlight-todo'
|
||||||
|
} else if (status === 7) {
|
||||||
|
// 审批通过中
|
||||||
|
return 'highlight-todo'
|
||||||
|
} else if (status === 0) {
|
||||||
|
// 待审批
|
||||||
|
return 'highlight-todo'
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const getActivityOutgoing = (activity) => {
|
||||||
|
// 如果有 outgoing,则直接使用它
|
||||||
|
if (activity.outgoing && activity.outgoing.length > 0) {
|
||||||
|
return activity.outgoing
|
||||||
|
}
|
||||||
|
// 如果没有,则遍历获得起点为它的【bpmn:SequenceFlow】节点们。原因是:bpmn-js 的 UserTask 拿不到 outgoing
|
||||||
|
const flowElements = bpmnModeler.getDefinitions().rootElements[0].flowElements
|
||||||
|
const outgoing: any[] = []
|
||||||
|
flowElements.forEach((item: any) => {
|
||||||
|
if (item.$type !== 'bpmn:SequenceFlow') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (item.sourceRef.id === activity.key) {
|
||||||
|
outgoing.push(item)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return outgoing
|
||||||
|
}
|
||||||
|
const initModelListeners = () => {
|
||||||
|
const EventBus = bpmnModeler.get('eventBus')
|
||||||
|
// 注册需要的监听事件
|
||||||
|
EventBus.on('element.hover', function (eventObj) {
|
||||||
|
let element = eventObj ? eventObj.element : null
|
||||||
|
elementHover(element)
|
||||||
|
})
|
||||||
|
EventBus.on('element.out', function (eventObj) {
|
||||||
|
let element = eventObj ? eventObj.element : null
|
||||||
|
elementOut(element)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 流程图的元素被 hover
|
||||||
|
const elementHover = (element) => {
|
||||||
|
element.value = element
|
||||||
|
!elementOverlayIds.value && (elementOverlayIds.value = {})
|
||||||
|
!overlays.value && (overlays.value = bpmnModeler.get('overlays'))
|
||||||
|
// 展示信息
|
||||||
|
// console.log(activityLists.value, 'activityLists.value')
|
||||||
|
// console.log(element.value, 'element.value')
|
||||||
|
const activity = activityLists.value.find((m) => m.key === element.value.id)
|
||||||
|
// console.log(activity, 'activityactivityactivityactivity')
|
||||||
|
if (!activity) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// if (!elementOverlayIds.value[element.value.id] && element.value.type !== 'bpmn:Process') {
|
||||||
|
// let html = `<div class="element-overlays">
|
||||||
|
// <p>Elemet id: ${element.value.id}</p>
|
||||||
|
// <p>Elemet type: ${element.value.type}</p>
|
||||||
|
// </div>` // 默认值
|
||||||
|
// if (element.value.type === 'bpmn:StartEvent' && processInstance.value) {
|
||||||
|
// html = `<p>发起人:${processInstance.value.startUser.nickname}</p>
|
||||||
|
// <p>部门:${processInstance.value.startUser.deptName}</p>
|
||||||
|
// <p>创建时间:${formatDate(processInstance.value.createTime)}`
|
||||||
|
// } else if (element.value.type === 'bpmn:UserTask') {
|
||||||
|
// let task = taskList.value.find((m) => m.id === activity.taskId) // 找到活动对应的 taskId
|
||||||
|
// if (!task) {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// // let optionData = getIntDictOptions(DICT_TYPE.BPM_TASK_STATUS)
|
||||||
|
// let dataResult = ''
|
||||||
|
// // optionData.forEach((element) => {
|
||||||
|
// // if (element.value == task.status) {
|
||||||
|
// // dataResult = element.label
|
||||||
|
// // }
|
||||||
|
// // })
|
||||||
|
// html = `<p>审批人:${task.assigneeUser.nickname}</p>
|
||||||
|
// <p>部门:${task.assigneeUser.deptName}</p>
|
||||||
|
// <p>结果:${dataResult}</p>
|
||||||
|
// <p>创建时间:${formatDate(task.createTime)}</p>`
|
||||||
|
// // html = `<p>审批人:${task.assigneeUser.nickname}</p>
|
||||||
|
// // <p>部门:${task.assigneeUser.deptName}</p>
|
||||||
|
// // <p>结果:${getIntDictOptions(
|
||||||
|
// // DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT,
|
||||||
|
// // task.status
|
||||||
|
// // )}</p>
|
||||||
|
// // <p>创建时间:${formatDate(task.createTime)}</p>`
|
||||||
|
// if (task.endTime) {
|
||||||
|
// html += `<p>结束时间:${formatDate(task.endTime)}</p>`
|
||||||
|
// }
|
||||||
|
// if (task.reason) {
|
||||||
|
// html += `<p>审批建议:${task.reason}</p>`
|
||||||
|
// }
|
||||||
|
// } else if (element.value.type === 'bpmn:ServiceTask' && processInstance.value) {
|
||||||
|
// if (activity.startTime > 0) {
|
||||||
|
// html = `<p>创建时间:${formatDate(activity.startTime)}</p>`
|
||||||
|
// }
|
||||||
|
// if (activity.endTime > 0) {
|
||||||
|
// html += `<p>结束时间:${formatDate(activity.endTime)}</p>`
|
||||||
|
// }
|
||||||
|
// console.log(html)
|
||||||
|
// } else if (element.value.type === 'bpmn:EndEvent' && processInstance.value) {
|
||||||
|
// // let optionData = getIntDictOptions(DICT_TYPE.BPM_TASK_STATUS)
|
||||||
|
// let dataResult = ''
|
||||||
|
// // optionData.forEach((element) => {
|
||||||
|
// // if (element.value == processInstance.value.status) {
|
||||||
|
// // dataResult = element.label
|
||||||
|
// // }
|
||||||
|
// // })
|
||||||
|
// html = `<p>结果:${dataResult}</p>`
|
||||||
|
// // html = `<p>结果:${getIntDictOptions(
|
||||||
|
// // DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT,
|
||||||
|
// // processInstance.value.status
|
||||||
|
// // )}</p>`
|
||||||
|
// if (processInstance.value.endTime) {
|
||||||
|
// html += `<p>结束时间:${formatDate(processInstance.value.endTime)}</p>`
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// // console.log(html, 'html111111111111111')
|
||||||
|
// elementOverlayIds.value[element.value.id] = toRaw(overlays.value)?.add(element.value, {
|
||||||
|
// position: { left: 0, bottom: 0 },
|
||||||
|
// html: `<div class="element-overlays">${html}</div>`
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 流程图的元素被 out
|
||||||
|
const elementOut = (element) => {
|
||||||
|
toRaw(overlays.value).remove({ element })
|
||||||
|
elementOverlayIds.value[element.id] = null
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
xml.value = props.value
|
||||||
|
activityLists.value = props.activityData
|
||||||
|
// 初始化
|
||||||
|
initBpmnModeler()
|
||||||
|
createNewDiagram(xml.value)
|
||||||
|
// 初始模型的监听器
|
||||||
|
initModelListeners()
|
||||||
|
})
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
// this.$once('hook:beforeDestroy', () => {
|
||||||
|
// })
|
||||||
|
if (bpmnModeler) bpmnModeler.destroy()
|
||||||
|
emit('destroy', bpmnModeler)
|
||||||
|
bpmnModeler = null
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.value,
|
||||||
|
(newValue) => {
|
||||||
|
xml.value = newValue
|
||||||
|
createNewDiagram(xml.value)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
watch(
|
||||||
|
() => props.activityData,
|
||||||
|
(newActivityData) => {
|
||||||
|
activityLists.value = newActivityData
|
||||||
|
createNewDiagram(xml.value)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
watch(
|
||||||
|
() => props.processInstanceData,
|
||||||
|
(newProcessInstanceData) => {
|
||||||
|
processInstance.value = newProcessInstanceData
|
||||||
|
createNewDiagram(xml.value)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
watch(
|
||||||
|
() => props.taskData,
|
||||||
|
(newTaskListData) => {
|
||||||
|
taskList.value = newTaskListData
|
||||||
|
createNewDiagram(xml.value)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
/** 处理中 */
|
||||||
|
.highlight-todo.djs-connection > .djs-visual > path {
|
||||||
|
stroke: #1890ff !important;
|
||||||
|
stroke-dasharray: 4px !important;
|
||||||
|
fill-opacity: 0.2 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight-todo.djs-shape .djs-visual > :nth-child(1) {
|
||||||
|
fill: #1890ff !important;
|
||||||
|
stroke: #1890ff !important;
|
||||||
|
stroke-dasharray: 4px !important;
|
||||||
|
fill-opacity: 0.2 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.highlight-todo.djs-connection > .djs-visual > path) {
|
||||||
|
stroke: #1890ff !important;
|
||||||
|
stroke-dasharray: 4px !important;
|
||||||
|
fill-opacity: 0.2 !important;
|
||||||
|
marker-end: url('#sequenceflow-end-_E7DFDF-_E7DFDF-803g1kf6zwzmcig1y2ulm5egr');
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.highlight-todo.djs-shape .djs-visual > :nth-child(1)) {
|
||||||
|
fill: #1890ff !important;
|
||||||
|
stroke: #1890ff !important;
|
||||||
|
stroke-dasharray: 4px !important;
|
||||||
|
fill-opacity: 0.2 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 通过 */
|
||||||
|
.highlight.djs-shape .djs-visual > :nth-child(1) {
|
||||||
|
fill: green !important;
|
||||||
|
stroke: green !important;
|
||||||
|
fill-opacity: 0.2 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight.djs-shape .djs-visual > :nth-child(2) {
|
||||||
|
fill: green !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight.djs-shape .djs-visual > path {
|
||||||
|
fill: green !important;
|
||||||
|
fill-opacity: 0.2 !important;
|
||||||
|
stroke: green !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight.djs-connection > .djs-visual > path {
|
||||||
|
stroke: green !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight:not(.djs-connection) .djs-visual > :nth-child(1) {
|
||||||
|
fill: green !important; /* color elements as green */
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.highlight.djs-shape .djs-visual > :nth-child(1)) {
|
||||||
|
fill: green !important;
|
||||||
|
stroke: green !important;
|
||||||
|
fill-opacity: 0.2 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.highlight.djs-shape .djs-visual > :nth-child(2)) {
|
||||||
|
fill: green !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.highlight.djs-shape .djs-visual > path) {
|
||||||
|
fill: green !important;
|
||||||
|
fill-opacity: 0.2 !important;
|
||||||
|
stroke: green !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.highlight.djs-connection > .djs-visual > path) {
|
||||||
|
stroke: green !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.djs-element.highlight > .djs-visual > path {
|
||||||
|
stroke: green !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 不通过 */
|
||||||
|
.highlight-reject.djs-shape .djs-visual > :nth-child(1) {
|
||||||
|
fill: red !important;
|
||||||
|
stroke: red !important;
|
||||||
|
fill-opacity: 0.2 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight-reject.djs-shape .djs-visual > :nth-child(2) {
|
||||||
|
fill: red !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight-reject.djs-shape .djs-visual > path {
|
||||||
|
fill: red !important;
|
||||||
|
fill-opacity: 0.2 !important;
|
||||||
|
stroke: red !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight-reject.djs-connection > .djs-visual > path {
|
||||||
|
stroke: red !important;
|
||||||
|
marker-end: url(#sequenceflow-end-white-success) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight-reject:not(.djs-connection) .djs-visual > :nth-child(1) {
|
||||||
|
fill: red !important; /* color elements as green */
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.highlight-reject.djs-shape .djs-visual > :nth-child(1)) {
|
||||||
|
fill: red !important;
|
||||||
|
stroke: red !important;
|
||||||
|
fill-opacity: 0.2 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.highlight-reject.djs-shape .djs-visual > :nth-child(2)) {
|
||||||
|
fill: red !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.highlight-reject.djs-shape .djs-visual > path) {
|
||||||
|
fill: red !important;
|
||||||
|
fill-opacity: 0.2 !important;
|
||||||
|
stroke: red !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.highlight-reject.djs-connection > .djs-visual > path) {
|
||||||
|
stroke: red !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 已取消 */
|
||||||
|
.highlight-cancel.djs-shape .djs-visual > :nth-child(1) {
|
||||||
|
fill: grey !important;
|
||||||
|
stroke: grey !important;
|
||||||
|
fill-opacity: 0.2 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight-cancel.djs-shape .djs-visual > :nth-child(2) {
|
||||||
|
fill: grey !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight-cancel.djs-shape .djs-visual > path {
|
||||||
|
fill: grey !important;
|
||||||
|
fill-opacity: 0.2 !important;
|
||||||
|
stroke: grey !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight-cancel.djs-connection > .djs-visual > path {
|
||||||
|
stroke: grey !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight-cancel:not(.djs-connection) .djs-visual > :nth-child(1) {
|
||||||
|
fill: grey !important; /* color elements as green */
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.highlight-cancel.djs-shape .djs-visual > :nth-child(1)) {
|
||||||
|
fill: grey !important;
|
||||||
|
stroke: grey !important;
|
||||||
|
fill-opacity: 0.2 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.highlight-cancel.djs-shape .djs-visual > :nth-child(2)) {
|
||||||
|
fill: grey !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.highlight-cancel.djs-shape .djs-visual > path) {
|
||||||
|
fill: grey !important;
|
||||||
|
fill-opacity: 0.2 !important;
|
||||||
|
stroke: grey !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.highlight-cancel.djs-connection > .djs-visual > path) {
|
||||||
|
stroke: grey !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 回退 */
|
||||||
|
.highlight-return.djs-shape .djs-visual > :nth-child(1) {
|
||||||
|
fill: #e6a23c !important;
|
||||||
|
stroke: #e6a23c !important;
|
||||||
|
fill-opacity: 0.2 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight-return.djs-shape .djs-visual > :nth-child(2) {
|
||||||
|
fill: #e6a23c !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight-return.djs-shape .djs-visual > path {
|
||||||
|
fill: #e6a23c !important;
|
||||||
|
fill-opacity: 0.2 !important;
|
||||||
|
stroke: #e6a23c !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight-return.djs-connection > .djs-visual > path {
|
||||||
|
stroke: #e6a23c !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight-return:not(.djs-connection) .djs-visual > :nth-child(1) {
|
||||||
|
fill: #e6a23c !important; /* color elements as green */
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.highlight-return.djs-shape .djs-visual > :nth-child(1)) {
|
||||||
|
fill: #e6a23c !important;
|
||||||
|
stroke: #e6a23c !important;
|
||||||
|
fill-opacity: 0.2 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.highlight-return.djs-shape .djs-visual > :nth-child(2)) {
|
||||||
|
fill: #e6a23c !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.highlight-return.djs-shape .djs-visual > path) {
|
||||||
|
fill: #e6a23c !important;
|
||||||
|
fill-opacity: 0.2 !important;
|
||||||
|
stroke: #e6a23c !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.highlight-return.djs-connection > .djs-visual > path) {
|
||||||
|
stroke: #e6a23c !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.element-overlays {
|
||||||
|
width: 200px;
|
||||||
|
padding: 8px;
|
||||||
|
color: #fafafa;
|
||||||
|
background: rgb(0 0 0 / 60%);
|
||||||
|
border-radius: 4px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import MyProcessDesigner from './ProcessDesigner.vue'
|
||||||
|
|
||||||
|
MyProcessDesigner.install = function (Vue) {
|
||||||
|
Vue.component(MyProcessDesigner.name, MyProcessDesigner)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 流程图的设计器,可编辑
|
||||||
|
export default MyProcessDesigner
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import MyProcessViewer from './ProcessViewer.vue'
|
||||||
|
|
||||||
|
MyProcessViewer.install = function (Vue) {
|
||||||
|
Vue.component(MyProcessViewer.name, MyProcessViewer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 流程图的查看器,不可编辑
|
||||||
|
export default MyProcessViewer
|
||||||
@@ -0,0 +1,423 @@
|
|||||||
|
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
|
||||||
|
) {
|
||||||
|
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) {
|
||||||
|
const context = event.context,
|
||||||
|
shape = context.shape
|
||||||
|
|
||||||
|
if (!hasPrimaryModifier(event) || !contextPad.isOpen(shape)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const 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) {
|
||||||
|
const contextPad = this._contextPad,
|
||||||
|
modeling = this._modeling,
|
||||||
|
elementFactory = this._elementFactory,
|
||||||
|
connect = this._connect,
|
||||||
|
create = this._create,
|
||||||
|
popupMenu = this._popupMenu,
|
||||||
|
canvas = this._canvas,
|
||||||
|
rules = this._rules,
|
||||||
|
autoPlace = this._autoPlace,
|
||||||
|
translate = this._translate
|
||||||
|
|
||||||
|
const actions = {}
|
||||||
|
|
||||||
|
if (element.type === 'label') {
|
||||||
|
return actions
|
||||||
|
}
|
||||||
|
|
||||||
|
const businessObject = element.businessObject
|
||||||
|
|
||||||
|
function startConnect(event, element) {
|
||||||
|
connect.start(event, element)
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeElement() {
|
||||||
|
modeling.removeElements([element])
|
||||||
|
}
|
||||||
|
|
||||||
|
function getReplaceMenuPosition(element) {
|
||||||
|
const Y_OFFSET = 5
|
||||||
|
|
||||||
|
const diagramContainer = canvas.getContainer(),
|
||||||
|
pad = contextPad.getPad(element).html
|
||||||
|
|
||||||
|
const diagramRect = diagramContainer.getBoundingClientRect(),
|
||||||
|
padRect = pad.getBoundingClientRect()
|
||||||
|
|
||||||
|
const top = padRect.top - diagramRect.top
|
||||||
|
const left = padRect.left - diagramRect.left
|
||||||
|
|
||||||
|
const 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) {
|
||||||
|
const shape = elementFactory.createShape(assign({ type: type }, options))
|
||||||
|
create.start(event, shape, {
|
||||||
|
source: element
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const append = autoPlace
|
||||||
|
? function (event, element) {
|
||||||
|
const 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)) {
|
||||||
|
const 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: '修改类型',
|
||||||
|
action: {
|
||||||
|
click: function (event, element) {
|
||||||
|
const 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
|
||||||
|
let 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) {
|
||||||
|
const isType = eventBo.$instanceOf(type)
|
||||||
|
let isDefinition = false
|
||||||
|
|
||||||
|
const definitions = eventBo.eventDefinitions || []
|
||||||
|
forEach(definitions, function (def) {
|
||||||
|
if (def.$type === definition) {
|
||||||
|
isDefinition = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return isType && isDefinition
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import CustomContextPadProvider from './contentPadProvider'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
__init__: ['contextPadProvider'],
|
||||||
|
contextPadProvider: ['type', CustomContextPadProvider]
|
||||||
|
}
|
||||||
@@ -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>`
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,83 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
import { some } from 'min-dash'
|
||||||
|
|
||||||
|
// const some = require('min-dash').some
|
||||||
|
// const some = some
|
||||||
|
|
||||||
|
const 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) {
|
||||||
|
const name = propDescriptor.name,
|
||||||
|
types = ALLOWED_TYPES[name.replace(/activiti:/, '')]
|
||||||
|
|
||||||
|
return name === propName && anyType(newElement, types)
|
||||||
|
}
|
||||||
|
|
||||||
|
function ActivitiModdleExtension(eventBus) {
|
||||||
|
eventBus.on(
|
||||||
|
'property.clone',
|
||||||
|
function (context) {
|
||||||
|
const newElement = context.newElement,
|
||||||
|
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')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// module.exports = ActivitiModdleExtension;
|
||||||
|
export default ActivitiModdleExtension
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
/*
|
||||||
|
* @author igdianov
|
||||||
|
* address https://github.com/igdianov/activiti-bpmn-moddle
|
||||||
|
* */
|
||||||
|
|
||||||
|
import activitiExtension from './activitiExtension'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
__init__: ['ActivitiModdleExtension'],
|
||||||
|
ActivitiModdleExtension: ['type', activitiExtension]
|
||||||
|
}
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
import { isFunction, isObject, some } from 'min-dash'
|
||||||
|
|
||||||
|
// const isFunction = isFunction,
|
||||||
|
// isObject = isObject,
|
||||||
|
// some = some
|
||||||
|
// const isFunction = require('min-dash').isFunction,
|
||||||
|
// isObject = require('min-dash').isObject,
|
||||||
|
// some = require('min-dash').some
|
||||||
|
|
||||||
|
const WILDCARD = '*'
|
||||||
|
|
||||||
|
function CamundaModdleExtension(eventBus) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||||
|
const self = this
|
||||||
|
|
||||||
|
eventBus.on('moddleCopy.canCopyProperty', function (context) {
|
||||||
|
const property = context.property,
|
||||||
|
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
|
||||||
|
const connector = getParent(parent, 'camunda:Connector')
|
||||||
|
|
||||||
|
if (connector) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// special rules inside bpmn:FlowNode
|
||||||
|
const flowNode = getParent(parent, 'bpmn:FlowNode')
|
||||||
|
|
||||||
|
if (!flowNode) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isAny(flowNode, ['bpmn:StartEvent', 'bpmn:Gateway', 'bpmn:BoundaryEvent'])) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return !(is(flowNode, 'bpmn:SubProcess') && flowNode.get('triggeredByEvent'))
|
||||||
|
}
|
||||||
|
|
||||||
|
CamundaModdleExtension.prototype.canHostConnector = function (parent) {
|
||||||
|
const 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) {
|
||||||
|
const callActivity = getParent(parent, 'bpmn:CallActivity')
|
||||||
|
|
||||||
|
if (callActivity) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
const 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// module.exports = CamundaModdleExtension;
|
||||||
|
export default CamundaModdleExtension
|
||||||
|
|
||||||
|
// 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
|
||||||
|
const descriptor = property.$type && property.$model.getTypeDescriptor(property.$type)
|
||||||
|
|
||||||
|
const 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
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
import extension from './extension'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
__init__: ['camundaModdleExtension'],
|
||||||
|
camundaModdleExtension: ['type', extension]
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
import { some } from 'min-dash'
|
||||||
|
|
||||||
|
// const some = some
|
||||||
|
// const some = require('min-dash').some
|
||||||
|
|
||||||
|
const 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) {
|
||||||
|
const name = propDescriptor.name,
|
||||||
|
types = ALLOWED_TYPES[name.replace(/flowable:/, '')]
|
||||||
|
|
||||||
|
return name === propName && anyType(newElement, types)
|
||||||
|
}
|
||||||
|
|
||||||
|
function FlowableModdleExtension(eventBus) {
|
||||||
|
eventBus.on(
|
||||||
|
'property.clone',
|
||||||
|
function (context) {
|
||||||
|
const newElement = context.newElement,
|
||||||
|
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')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// module.exports = FlowableModdleExtension;
|
||||||
|
export default FlowableModdleExtension
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
/*
|
||||||
|
* @author igdianov
|
||||||
|
* address https://github.com/igdianov/activiti-bpmn-moddle
|
||||||
|
* */
|
||||||
|
import flowableExtension from './flowableExtension'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
__init__: ['FlowableModdleExtension'],
|
||||||
|
FlowableModdleExtension: ['type', flowableExtension]
|
||||||
|
}
|
||||||
@@ -0,0 +1,221 @@
|
|||||||
|
import PaletteProvider from 'bpmn-js/lib/features/palette/PaletteProvider'
|
||||||
|
import { assign } from 'min-dash'
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
// 利用中介函数重写原型链方法
|
||||||
|
F.prototype.getPaletteEntries = function () {
|
||||||
|
const actions = {},
|
||||||
|
create = this._create,
|
||||||
|
elementFactory = this._elementFactory,
|
||||||
|
spaceTool = this._spaceTool,
|
||||||
|
lassoTool = this._lassoTool,
|
||||||
|
handTool = this._handTool,
|
||||||
|
globalConnect = this._globalConnect,
|
||||||
|
translate = this._translate
|
||||||
|
|
||||||
|
function createAction(type, group, className, title, options) {
|
||||||
|
function createListener(event) {
|
||||||
|
const shape = elementFactory.createShape(assign({ type: type }, options))
|
||||||
|
|
||||||
|
if (options) {
|
||||||
|
shape.businessObject.di.isExpanded = options.isExpanded
|
||||||
|
}
|
||||||
|
|
||||||
|
create.start(event, shape)
|
||||||
|
}
|
||||||
|
|
||||||
|
const shortType = type.replace(/^bpmn:/, '')
|
||||||
|
|
||||||
|
return {
|
||||||
|
group: group,
|
||||||
|
className: className,
|
||||||
|
title: title || translate('Create {type}', { type: shortType }),
|
||||||
|
action: {
|
||||||
|
dragstart: createListener,
|
||||||
|
click: createListener
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createSubprocess(event) {
|
||||||
|
const subProcess = elementFactory.createShape({
|
||||||
|
type: 'bpmn:SubProcess',
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
isExpanded: true
|
||||||
|
})
|
||||||
|
|
||||||
|
const 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: '激活抓手工具',
|
||||||
|
// 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的构造器指向,防止原型链的混乱;
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
// import PaletteModule from "diagram-js/lib/features/palette";
|
||||||
|
// import CreateModule from "diagram-js/lib/features/create";
|
||||||
|
// import SpaceToolModule from "diagram-js/lib/features/space-tool";
|
||||||
|
// import LassoToolModule from "diagram-js/lib/features/lasso-tool";
|
||||||
|
// import HandToolModule from "diagram-js/lib/features/hand-tool";
|
||||||
|
// import GlobalConnectModule from "diagram-js/lib/features/global-connect";
|
||||||
|
// import translate from "diagram-js/lib/i18n/translate";
|
||||||
|
//
|
||||||
|
// import PaletteProvider from "./paletteProvider";
|
||||||
|
//
|
||||||
|
// export default {
|
||||||
|
// __depends__: [PaletteModule, CreateModule, SpaceToolModule, LassoToolModule, HandToolModule, GlobalConnectModule, translate],
|
||||||
|
// __init__: ["paletteProvider"],
|
||||||
|
// paletteProvider: ["type", PaletteProvider]
|
||||||
|
// };
|
||||||
|
|
||||||
|
import CustomPalette from './CustomPalette'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
__init__: ['paletteProvider'],
|
||||||
|
paletteProvider: ['type', CustomPalette]
|
||||||
|
}
|
||||||
@@ -0,0 +1,213 @@
|
|||||||
|
import { assign } from 'min-dash'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 () {
|
||||||
|
const actions = {},
|
||||||
|
create = this._create,
|
||||||
|
elementFactory = this._elementFactory,
|
||||||
|
spaceTool = this._spaceTool,
|
||||||
|
lassoTool = this._lassoTool,
|
||||||
|
handTool = this._handTool,
|
||||||
|
globalConnect = this._globalConnect,
|
||||||
|
translate = this._translate
|
||||||
|
|
||||||
|
function createAction(type, group, className, title, options) {
|
||||||
|
function createListener(event) {
|
||||||
|
const shape = elementFactory.createShape(assign({ type: type }, options))
|
||||||
|
|
||||||
|
if (options) {
|
||||||
|
shape.businessObject.di.isExpanded = options.isExpanded
|
||||||
|
}
|
||||||
|
|
||||||
|
create.start(event, shape)
|
||||||
|
}
|
||||||
|
|
||||||
|
const shortType = type.replace(/^bpmn:/, '')
|
||||||
|
|
||||||
|
return {
|
||||||
|
group: group,
|
||||||
|
className: className,
|
||||||
|
title: title || translate('Create {type}', { type: shortType }),
|
||||||
|
action: {
|
||||||
|
dragstart: createListener,
|
||||||
|
click: createListener
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createSubprocess(event) {
|
||||||
|
const subProcess = elementFactory.createShape({
|
||||||
|
type: 'bpmn:SubProcess',
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
isExpanded: true
|
||||||
|
})
|
||||||
|
|
||||||
|
const 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
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
// 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 + '}'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,240 @@
|
|||||||
|
/**
|
||||||
|
* 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': '代理表达式',
|
||||||
|
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: '代理人',
|
||||||
|
'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} ,或者一个ISO标准日期,如: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} ,或者一个ISO标准日期,如: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.': '指定多个组作为逗号分隔的列表。'
|
||||||
|
}
|
||||||
11
src/components/bpmnProcessDesigner/package/index.ts
Normal file
11
src/components/bpmnProcessDesigner/package/index.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import MyProcessDesigner from './designer'
|
||||||
|
import MyProcessPenal from './penal'
|
||||||
|
// import MyProcessViewer from './designer/index2'
|
||||||
|
|
||||||
|
import './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'
|
||||||
|
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css'
|
||||||
|
|
||||||
|
export { MyProcessDesigner,MyProcessPenal }
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
<template>
|
||||||
|
<div class="my-process-palette">
|
||||||
|
<div class="test-button" @click="addTask" @mousedown="addTask">测试任务</div>
|
||||||
|
<div class="test-container" id="palette-container">1</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { assign } from 'min-dash'
|
||||||
|
|
||||||
|
defineOptions({ name: 'MyProcessPalette' })
|
||||||
|
|
||||||
|
const bpmnInstances = () => (window as any).bpmnInstances
|
||||||
|
const addTask = (event, options: any = {}) => {
|
||||||
|
const ElementFactory = bpmnInstances().elementFactory
|
||||||
|
const create = bpmnInstances().modeler.get('create')
|
||||||
|
|
||||||
|
console.log(ElementFactory, create)
|
||||||
|
|
||||||
|
const shape = ElementFactory.createShape(assign({ type: 'bpmn:UserTask' }, options))
|
||||||
|
|
||||||
|
if (options) {
|
||||||
|
shape.businessObject.di.isExpanded = options.isExpanded
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(event, 'event')
|
||||||
|
console.log(shape, 'shape')
|
||||||
|
create.start(event, shape)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.my-process-palette {
|
||||||
|
padding: 80px 20px 20px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
.test-button {
|
||||||
|
padding: 8px 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 1px solid rgb(24 144 255 / 80%);
|
||||||
|
border-radius: 4px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,233 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="process-panel__container"
|
||||||
|
:style="{ width: `${width}px`, height: `700px`, overflow: 'auto', background: '#fff' }"
|
||||||
|
>
|
||||||
|
<el-collapse v-model="activeTab">
|
||||||
|
<el-collapse-item name="base">
|
||||||
|
<!-- class="panel-tab__title" -->
|
||||||
|
<template #title>
|
||||||
|
<Icon name="el-icon-InfoFilled" />
|
||||||
|
常规
|
||||||
|
</template>
|
||||||
|
<ElementBaseInfo
|
||||||
|
:id-edit-disabled="idEditDisabled"
|
||||||
|
:business-object="elementBusinessObject"
|
||||||
|
:type="elementType"
|
||||||
|
:model="model"
|
||||||
|
/>
|
||||||
|
</el-collapse-item>
|
||||||
|
<!-- <el-collapse-item name="condition" v-if="elementType === 'Process'" key="message">
|
||||||
|
<template #title>
|
||||||
|
<Icon name="el-icon-Comment" />
|
||||||
|
消息与信号
|
||||||
|
</template>
|
||||||
|
<SignalAndMassage />
|
||||||
|
</el-collapse-item>
|
||||||
|
<el-collapse-item name="condition" v-if="conditionFormVisible" key="condition">
|
||||||
|
<template #title>
|
||||||
|
<Icon name="el-icon-Promotion" />
|
||||||
|
流转条件
|
||||||
|
</template>
|
||||||
|
<FlowCondition :business-object="elementBusinessObject" :type="elementType" />
|
||||||
|
</el-collapse-item>
|
||||||
|
<el-collapse-item name="condition" v-if="formVisible" key="form">
|
||||||
|
<template #title>
|
||||||
|
<Icon name="el-icon-List" />
|
||||||
|
表单
|
||||||
|
</template>
|
||||||
|
<ElementForm :id="elementId" :type="elementType" />
|
||||||
|
</el-collapse-item>
|
||||||
|
<el-collapse-item name="task" v-if="elementType.indexOf('Task') !== -1" key="task">
|
||||||
|
<template #title>
|
||||||
|
<Icon name="el-icon-Checked" />
|
||||||
|
任务(审批人)
|
||||||
|
</template>
|
||||||
|
<ElementTask :id="elementId" :type="elementType" />
|
||||||
|
</el-collapse-item>
|
||||||
|
<el-collapse-item name="multiInstance" v-if="elementType.indexOf('Task') !== -1" key="multiInstance">
|
||||||
|
<template #title>
|
||||||
|
<Icon name="el-icon-HelpFilled" />
|
||||||
|
多实例(会签配置)
|
||||||
|
</template>
|
||||||
|
<ElementMultiInstance :business-object="elementBusinessObject" :type="elementType" />
|
||||||
|
</el-collapse-item>
|
||||||
|
<el-collapse-item name="listeners" key="listeners">
|
||||||
|
<template #title>
|
||||||
|
<Icon name="el-icon-BellFilled" />
|
||||||
|
执行监听器
|
||||||
|
</template>
|
||||||
|
<ElementListeners :id="elementId" :type="elementType" />
|
||||||
|
</el-collapse-item>
|
||||||
|
<el-collapse-item name="taskListeners" v-if="elementType === 'UserTask'" key="taskListeners">
|
||||||
|
<template #title>
|
||||||
|
<Icon name="el-icon-BellFilled" />
|
||||||
|
任务监听器
|
||||||
|
</template>
|
||||||
|
<UserTaskListeners :id="elementId" :type="elementType" />
|
||||||
|
</el-collapse-item>
|
||||||
|
<el-collapse-item name="extensions" key="extensions">
|
||||||
|
<template #title>
|
||||||
|
<Icon name="el-icon-CirclePlusFilled" />
|
||||||
|
扩展属性
|
||||||
|
</template>
|
||||||
|
<ElementProperties :id="elementId" :type="elementType" />
|
||||||
|
</el-collapse-item> -->
|
||||||
|
<el-collapse-item name="other" key="other">
|
||||||
|
<template #title>
|
||||||
|
<Icon name="el-icon-Promotion" />
|
||||||
|
其他
|
||||||
|
</template>
|
||||||
|
<ElementOtherConfig :id="elementId" />
|
||||||
|
</el-collapse-item>
|
||||||
|
</el-collapse>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { onMounted, provide, ref, watch, onBeforeUnmount } from 'vue'
|
||||||
|
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'
|
||||||
|
|
||||||
|
defineOptions({ name: 'MyPropertiesPanel' })
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 侧边栏
|
||||||
|
* @Author MiyueFE
|
||||||
|
* @Home https://github.com/miyuesc
|
||||||
|
* @Date 2021年3月31日18:57:51
|
||||||
|
*/
|
||||||
|
const props = defineProps({
|
||||||
|
bpmnModeler: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
},
|
||||||
|
prefix: {
|
||||||
|
type: String,
|
||||||
|
default: 'camunda'
|
||||||
|
},
|
||||||
|
width: {
|
||||||
|
type: Number,
|
||||||
|
default: 480
|
||||||
|
},
|
||||||
|
idEditDisabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
model: Object // 流程模型的数据
|
||||||
|
})
|
||||||
|
|
||||||
|
const activeTab = ref('base')
|
||||||
|
const elementId = ref('')
|
||||||
|
const elementType = ref('')
|
||||||
|
const elementBusinessObject = ref<any>({}) // 元素 businessObject 镜像,提供给需要做判断的组件使用
|
||||||
|
const conditionFormVisible = ref(false) // 流转条件设置
|
||||||
|
const formVisible = ref(false) // 表单配置
|
||||||
|
const bpmnElement = ref()
|
||||||
|
|
||||||
|
provide('prefix', props.prefix)
|
||||||
|
provide('width', props.width)
|
||||||
|
const bpmnInstances = () => (window as any)?.bpmnInstances
|
||||||
|
|
||||||
|
// 监听 props.bpmnModeler 然后 initModels
|
||||||
|
const unwatchBpmn = watch(
|
||||||
|
() => props.bpmnModeler,
|
||||||
|
() => {
|
||||||
|
// 避免加载时 流程图 并未加载完成
|
||||||
|
if (!props.bpmnModeler) {
|
||||||
|
console.log('缺少props.bpmnModeler')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('props.bpmnModeler 有值了!!!')
|
||||||
|
const w = window as any
|
||||||
|
w.bpmnInstances = {
|
||||||
|
modeler: props.bpmnModeler,
|
||||||
|
modeling: props.bpmnModeler.get('modeling'),
|
||||||
|
moddle: props.bpmnModeler.get('moddle'),
|
||||||
|
eventBus: props.bpmnModeler.get('eventBus'),
|
||||||
|
bpmnFactory: props.bpmnModeler.get('bpmnFactory'),
|
||||||
|
elementFactory: props.bpmnModeler.get('elementFactory'),
|
||||||
|
elementRegistry: props.bpmnModeler.get('elementRegistry'),
|
||||||
|
replace: props.bpmnModeler.get('replace'),
|
||||||
|
selection: props.bpmnModeler.get('selection')
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(bpmnInstances(), 'window.bpmnInstances')
|
||||||
|
getActiveElement()
|
||||||
|
unwatchBpmn()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const getActiveElement = () => {
|
||||||
|
// 初始第一个选中元素 bpmn:Process
|
||||||
|
initFormOnChanged(null)
|
||||||
|
props.bpmnModeler.on('import.done', e => {
|
||||||
|
console.log(e, 'eeeee')
|
||||||
|
initFormOnChanged(null)
|
||||||
|
})
|
||||||
|
// 监听选择事件,修改当前激活的元素以及表单
|
||||||
|
props.bpmnModeler.on('selection.changed', ({ newSelection }) => {
|
||||||
|
initFormOnChanged(newSelection[0] || null)
|
||||||
|
})
|
||||||
|
props.bpmnModeler.on('element.changed', ({ element }) => {
|
||||||
|
// 保证 修改 "默认流转路径" 类似需要修改多个元素的事件发生的时候,更新表单的元素与原选中元素不一致。
|
||||||
|
if (element && element.id === elementId.value) {
|
||||||
|
initFormOnChanged(element)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 初始化数据
|
||||||
|
const initFormOnChanged = element => {
|
||||||
|
let activatedElement = element
|
||||||
|
if (!activatedElement) {
|
||||||
|
activatedElement =
|
||||||
|
bpmnInstances().elementRegistry.find(el => el.type === 'bpmn:Process') ??
|
||||||
|
bpmnInstances().elementRegistry.find(el => el.type === 'bpmn:Collaboration')
|
||||||
|
}
|
||||||
|
if (!activatedElement) return
|
||||||
|
console.log(`
|
||||||
|
----------
|
||||||
|
select element changed:
|
||||||
|
id: ${activatedElement.id}
|
||||||
|
type: ${activatedElement.businessObject.$type}
|
||||||
|
----------
|
||||||
|
`)
|
||||||
|
console.log('businessObject: ', activatedElement.businessObject)
|
||||||
|
bpmnInstances().bpmnElement = activatedElement
|
||||||
|
bpmnElement.value = activatedElement
|
||||||
|
elementId.value = activatedElement.id
|
||||||
|
elementType.value = activatedElement.type.split(':')[1] || ''
|
||||||
|
elementBusinessObject.value = JSON.parse(JSON.stringify(activatedElement.businessObject))
|
||||||
|
conditionFormVisible.value = !!(
|
||||||
|
elementType.value === 'SequenceFlow' &&
|
||||||
|
activatedElement.source &&
|
||||||
|
activatedElement.source.type.indexOf('StartEvent') === -1
|
||||||
|
)
|
||||||
|
formVisible.value = elementType.value === 'UserTask' || elementType.value === 'StartEvent'
|
||||||
|
}
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
const w = window as any
|
||||||
|
w.bpmnInstances = null
|
||||||
|
console.log(props, 'props1')
|
||||||
|
console.log(props.bpmnModeler, 'props.bpmnModeler1')
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => elementId.value,
|
||||||
|
() => {
|
||||||
|
activeTab.value = 'base'
|
||||||
|
}
|
||||||
|
)
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,183 @@
|
|||||||
|
<template>
|
||||||
|
<div class="panel-tab__content">
|
||||||
|
<el-form label-width="90px" :model="needProps" :rules="rules">
|
||||||
|
<div v-if="needProps.type == 'bpmn:Process'">
|
||||||
|
<!-- <el-form-item label="流程标识" prop="id">
|
||||||
|
<el-input
|
||||||
|
v-model="needProps.id"
|
||||||
|
placeholder="请输入流标标识"
|
||||||
|
:disabled="needProps.id !== undefined && needProps.id.length > 0"
|
||||||
|
@change="handleKeyUpdate"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="流程名称" prop="name">
|
||||||
|
<el-input
|
||||||
|
v-model="needProps.name"
|
||||||
|
placeholder="请输入流程名称"
|
||||||
|
clearable
|
||||||
|
@change="handleNameUpdate"
|
||||||
|
/>
|
||||||
|
</el-form-item> -->
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<el-form-item label="ID">
|
||||||
|
<el-input v-model="elementBaseInfo.id" clearable @change="updateBaseInfo('id')" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="名称">
|
||||||
|
<el-input v-model="elementBaseInfo.name" clearable @change="updateBaseInfo('name')" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="描述">
|
||||||
|
<el-input v-model="elementBaseInfo.documentation" clearable @change="updateBaseInfo('documentation')" />
|
||||||
|
</el-form-item>
|
||||||
|
</div>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { onMounted, reactive, ref, watch, onBeforeUnmount,toRaw } from 'vue'
|
||||||
|
defineOptions({ name: 'ElementBaseInfo' })
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
businessObject: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
},
|
||||||
|
model: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const needProps = ref<any>({})
|
||||||
|
const bpmnElement = ref()
|
||||||
|
const elementBaseInfo = ref<any>({})
|
||||||
|
// 流程表单的下拉框的数据
|
||||||
|
// const forms = ref([])
|
||||||
|
// 流程模型的校验
|
||||||
|
const rules = reactive({
|
||||||
|
id: [{ required: true, message: '流程标识不能为空', trigger: 'blur' }],
|
||||||
|
name: [{ required: true, message: '流程名称不能为空', trigger: 'blur' }]
|
||||||
|
})
|
||||||
|
|
||||||
|
const bpmnInstances = () => (window as any)?.bpmnInstances
|
||||||
|
const resetBaseInfo = () => {
|
||||||
|
console.log(window, 'window')
|
||||||
|
console.log(bpmnElement.value, 'bpmnElement')
|
||||||
|
|
||||||
|
bpmnElement.value = bpmnInstances()?.bpmnElement
|
||||||
|
// console.log(bpmnElement.value, 'resetBaseInfo11111111111')
|
||||||
|
elementBaseInfo.value = bpmnElement.value.businessObject
|
||||||
|
needProps.value['type'] = bpmnElement.value.businessObject.$type
|
||||||
|
// elementBaseInfo.value['typess'] = bpmnElement.value.businessObject.$type
|
||||||
|
|
||||||
|
// elementBaseInfo.value = JSON.parse(JSON.stringify(bpmnElement.value.businessObject))
|
||||||
|
// console.log(elementBaseInfo.value, 'elementBaseInfo22222222222')
|
||||||
|
}
|
||||||
|
const handleKeyUpdate = (value) => {
|
||||||
|
// 校验 value 的值,只有 XML NCName 通过的情况下,才进行赋值。否则,会导致流程图报错,无法绘制的问题
|
||||||
|
if (!value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!value.match(/[a-zA-Z_][\-_.0-9a-zA-Z$]*/)) {
|
||||||
|
console.log('key 不满足 XML NCName 规则,所以不进行赋值')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.log('key 满足 XML NCName 规则,所以进行赋值')
|
||||||
|
|
||||||
|
// 在 BPMN 的 XML 中,流程标识 key,其实对应的是 id 节点
|
||||||
|
elementBaseInfo.value['id'] = value
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
updateBaseInfo('id')
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
const handleNameUpdate = (value) => {
|
||||||
|
console.log(elementBaseInfo, 'elementBaseInfo')
|
||||||
|
if (!value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
elementBaseInfo.value['name'] = value
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
updateBaseInfo('name')
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
// const handleDescriptionUpdate=(value)=> {
|
||||||
|
// TODO 芋艿:documentation 暂时无法修改,后续在看看
|
||||||
|
// this.elementBaseInfo['documentation'] = value;
|
||||||
|
// this.updateBaseInfo('documentation');
|
||||||
|
// }
|
||||||
|
const updateBaseInfo = (key) => {
|
||||||
|
console.log(key, 'key')
|
||||||
|
// 触发 elementBaseInfo 对应的字段
|
||||||
|
const attrObj = Object.create(null)
|
||||||
|
// console.log(attrObj, 'attrObj')
|
||||||
|
attrObj[key] = elementBaseInfo.value[key]
|
||||||
|
// console.log(attrObj, 'attrObj111')
|
||||||
|
// const attrObj = {
|
||||||
|
// id: elementBaseInfo.value[key]
|
||||||
|
// // di: { id: `${elementBaseInfo.value[key]}_di` }
|
||||||
|
// }
|
||||||
|
// console.log(elementBaseInfo, 'elementBaseInfo11111111111')
|
||||||
|
needProps.value = { ...elementBaseInfo.value, ...needProps.value }
|
||||||
|
|
||||||
|
if (key === 'id') {
|
||||||
|
// console.log('jinru')
|
||||||
|
console.log(window, 'window')
|
||||||
|
console.log(bpmnElement.value, 'bpmnElement')
|
||||||
|
console.log(toRaw(bpmnElement.value), 'bpmnElement')
|
||||||
|
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
|
||||||
|
id: elementBaseInfo.value[key],
|
||||||
|
di: { id: `${elementBaseInfo.value[key]}_di` }
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
console.log(attrObj, 'attrObj')
|
||||||
|
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), attrObj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.businessObject,
|
||||||
|
(val) => {
|
||||||
|
// console.log(val, 'val11111111111111111111')
|
||||||
|
if (val) {
|
||||||
|
// nextTick(() => {
|
||||||
|
resetBaseInfo()
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.model?.key,
|
||||||
|
(val) => {
|
||||||
|
// 针对上传的 bpmn 流程图时,保证 key 和 name 的更新
|
||||||
|
if (val) {
|
||||||
|
handleKeyUpdate(props.model.key)
|
||||||
|
handleNameUpdate(props.model.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// watch(
|
||||||
|
// () => ({ ...props }),
|
||||||
|
// (oldVal, newVal) => {
|
||||||
|
// console.log(oldVal, 'oldVal')
|
||||||
|
// console.log(newVal, 'newVal')
|
||||||
|
// if (newVal) {
|
||||||
|
// needProps.value = newVal
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// immediate: true
|
||||||
|
// }
|
||||||
|
// )
|
||||||
|
// 'model.key': {
|
||||||
|
// immediate: false,
|
||||||
|
// handler: function (val) {
|
||||||
|
// this.handleKeyUpdate(val)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
bpmnElement.value = null
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,192 @@
|
|||||||
|
<template>
|
||||||
|
<div class="panel-tab__content">
|
||||||
|
<el-form :model="flowConditionForm" label-width="90px" size="small">
|
||||||
|
<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"
|
||||||
|
style="width: 192px"
|
||||||
|
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 lang="ts" setup>
|
||||||
|
import { onMounted, reactive, ref, watch, onBeforeUnmount,toRaw,nextTick } from 'vue'
|
||||||
|
defineOptions({ name: 'FlowCondition' })
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
businessObject: Object,
|
||||||
|
type: String
|
||||||
|
})
|
||||||
|
const flowConditionForm = ref<any>({})
|
||||||
|
const bpmnElement = ref()
|
||||||
|
const bpmnElementSource = ref()
|
||||||
|
const bpmnElementSourceRef = ref()
|
||||||
|
const flowConditionRef = ref()
|
||||||
|
const bpmnInstances = () => (window as any)?.bpmnInstances
|
||||||
|
const resetFlowCondition = () => {
|
||||||
|
bpmnElement.value = bpmnInstances().bpmnElement
|
||||||
|
bpmnElementSource.value = bpmnElement.value.source
|
||||||
|
bpmnElementSourceRef.value = bpmnElement.value.businessObject.sourceRef
|
||||||
|
if (
|
||||||
|
bpmnElementSourceRef.value &&
|
||||||
|
bpmnElementSourceRef.value.default &&
|
||||||
|
bpmnElementSourceRef.value.default.id === bpmnElement.value.id &&
|
||||||
|
flowConditionForm.value.type == 'default'
|
||||||
|
) {
|
||||||
|
// 默认
|
||||||
|
flowConditionForm.value = { type: 'default' }
|
||||||
|
} else if (!bpmnElement.value.businessObject.conditionExpression) {
|
||||||
|
// 普通
|
||||||
|
flowConditionForm.value = { type: 'normal' }
|
||||||
|
} else {
|
||||||
|
// 带条件
|
||||||
|
const conditionExpression = bpmnElement.value.businessObject.conditionExpression
|
||||||
|
flowConditionForm.value = { ...conditionExpression, type: 'condition' }
|
||||||
|
// resource 可直接标识 是否是外部资源脚本
|
||||||
|
if (flowConditionForm.value.resource) {
|
||||||
|
// this.$set(this.flowConditionForm, "conditionType", "script");
|
||||||
|
// this.$set(this.flowConditionForm, "scriptType", "externalScript");
|
||||||
|
flowConditionForm.value['conditionType'] = 'script'
|
||||||
|
flowConditionForm.value['scriptType'] = 'externalScript'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (conditionExpression.language) {
|
||||||
|
// this.$set(this.flowConditionForm, "conditionType", "script");
|
||||||
|
// this.$set(this.flowConditionForm, "scriptType", "inlineScript");
|
||||||
|
flowConditionForm.value['conditionType'] = 'script'
|
||||||
|
flowConditionForm.value['scriptType'] = 'inlineScript'
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// this.$set(this.flowConditionForm, "conditionType", "expression");
|
||||||
|
flowConditionForm.value['conditionType'] = 'expression'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const updateFlowType = (flowType) => {
|
||||||
|
// 正常条件类
|
||||||
|
if (flowType === 'condition') {
|
||||||
|
flowConditionRef.value = bpmnInstances().moddle.create('bpmn:FormalExpression')
|
||||||
|
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
|
||||||
|
conditionExpression: flowConditionRef.value
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 默认路径
|
||||||
|
if (flowType === 'default') {
|
||||||
|
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
|
||||||
|
conditionExpression: null
|
||||||
|
})
|
||||||
|
bpmnInstances().modeling.updateProperties(toRaw(bpmnElementSource.value), {
|
||||||
|
default: bpmnElement.value
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 正常路径,如果来源节点的默认路径是当前连线时,清除父元素的默认路径配置
|
||||||
|
if (
|
||||||
|
bpmnElementSourceRef.value.default &&
|
||||||
|
bpmnElementSourceRef.value.default.id === bpmnElement.value.id
|
||||||
|
) {
|
||||||
|
bpmnInstances().modeling.updateProperties(toRaw(bpmnElementSource.value), {
|
||||||
|
default: null
|
||||||
|
})
|
||||||
|
}
|
||||||
|
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
|
||||||
|
conditionExpression: null
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const updateFlowCondition = () => {
|
||||||
|
let { conditionType, scriptType, body, resource, language } = flowConditionForm.value
|
||||||
|
let condition
|
||||||
|
if (conditionType === 'expression') {
|
||||||
|
condition = bpmnInstances().moddle.create('bpmn:FormalExpression', { body })
|
||||||
|
} else {
|
||||||
|
if (scriptType === 'inlineScript') {
|
||||||
|
condition = bpmnInstances().moddle.create('bpmn:FormalExpression', { body, language })
|
||||||
|
// this.$set(this.flowConditionForm, "resource", "");
|
||||||
|
flowConditionForm.value['resource'] = ''
|
||||||
|
} else {
|
||||||
|
// this.$set(this.flowConditionForm, "body", "");
|
||||||
|
flowConditionForm.value['body'] = ''
|
||||||
|
condition = bpmnInstances().moddle.create('bpmn:FormalExpression', {
|
||||||
|
resource,
|
||||||
|
language
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
|
||||||
|
conditionExpression: condition
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
bpmnElement.value = null
|
||||||
|
bpmnElementSource.value = null
|
||||||
|
bpmnElementSourceRef.value = null
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.businessObject,
|
||||||
|
(val) => {
|
||||||
|
console.log(val, 'val')
|
||||||
|
nextTick(() => {
|
||||||
|
resetFlowCondition()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,479 @@
|
|||||||
|
<template>
|
||||||
|
<div class="panel-tab__content">
|
||||||
|
<el-form label-width="80px">
|
||||||
|
<el-form-item label="流程表单">
|
||||||
|
<!-- <el-input v-model="formKey" clearable @change="updateElementFormKey" />-->
|
||||||
|
<el-select v-model="formKey" clearable @change="updateElementFormKey">
|
||||||
|
<el-option v-for="form in formList" :key="form.id" :label="form.name" :value="form.id" />
|
||||||
|
</el-select>
|
||||||
|
</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><Icon icon="ep:coin" /> 表单字段</el-divider>-->
|
||||||
|
<!-- <el-table :data="fieldList" max-height="240" fit border>-->
|
||||||
|
<!-- <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 #default="scope">-->
|
||||||
|
<!-- <el-button type="primary" link @click="openFieldForm(scope, scope.$index)"-->
|
||||||
|
<!-- >编辑</el-button-->
|
||||||
|
<!-- >-->
|
||||||
|
<!-- <el-divider direction="vertical" />-->
|
||||||
|
<!-- <el-button-->
|
||||||
|
<!-- type="primary"-->
|
||||||
|
<!-- link-->
|
||||||
|
<!-- style="color: #ff4d4f"-->
|
||||||
|
<!-- @click="removeField(scope, scope.$index)"-->
|
||||||
|
<!-- >移除</el-button-->
|
||||||
|
<!-- >-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- </el-table-column>-->
|
||||||
|
<!-- </el-table>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- <div class="element-drawer__button">-->
|
||||||
|
<!-- <XButton type="primary" proIcon="ep:plus" title="添加字段" @click="openFieldForm(null, -1)" />-->
|
||||||
|
<!-- </div>-->
|
||||||
|
|
||||||
|
<!--字段配置侧边栏-->
|
||||||
|
<!-- <el-drawer-->
|
||||||
|
<!-- v-model="fieldModelVisible"-->
|
||||||
|
<!-- title="字段配置"-->
|
||||||
|
<!-- :size="`${width}px`"-->
|
||||||
|
<!-- append-to-body-->
|
||||||
|
<!-- destroy-on-close-->
|
||||||
|
<!-- >-->
|
||||||
|
<!-- <el-form :model="formFieldForm" label-width="90px">-->
|
||||||
|
<!-- <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" :label="value" :value="key" :key="key" />-->
|
||||||
|
<!-- </el-select>-->
|
||||||
|
<!-- </el-form-item>-->
|
||||||
|
<!-- <el-form-item label="类型名称" v-if="formFieldForm.typeType === 'custom'">-->
|
||||||
|
<!-- <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 label="时间格式" v-if="formFieldForm.typeType === 'date'">-->
|
||||||
|
<!-- <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 class="listener-filed__title" key="enum-title">-->
|
||||||
|
<!-- <span><Icon icon="ep:menu" />枚举值列表:</span>-->
|
||||||
|
<!-- <el-button type="primary" @click="openFieldOptionForm(null, -1, 'enum')"-->
|
||||||
|
<!-- >添加枚举值</el-button-->
|
||||||
|
<!-- >-->
|
||||||
|
<!-- </p>-->
|
||||||
|
<!-- <el-table :data="fieldEnumList" key="enum-table" max-height="240" fit border>-->
|
||||||
|
<!-- <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 #default="scope">-->
|
||||||
|
<!-- <el-button-->
|
||||||
|
<!-- type="primary"-->
|
||||||
|
<!-- link-->
|
||||||
|
<!-- @click="openFieldOptionForm(scope, scope.$index, 'enum')"-->
|
||||||
|
<!-- >编辑</el-button-->
|
||||||
|
<!-- >-->
|
||||||
|
<!-- <el-divider direction="vertical" />-->
|
||||||
|
<!-- <el-button-->
|
||||||
|
<!-- type="primary"-->
|
||||||
|
<!-- link-->
|
||||||
|
<!-- style="color: #ff4d4f"-->
|
||||||
|
<!-- @click="removeFieldOptionItem(scope, scope.$index, 'enum')"-->
|
||||||
|
<!-- >移除</el-button-->
|
||||||
|
<!-- >-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- </el-table-column>-->
|
||||||
|
<!-- </el-table>-->
|
||||||
|
<!-- </template>-->
|
||||||
|
|
||||||
|
<!-- <!– 校验规则 –>-->
|
||||||
|
<!-- <el-divider key="validation-divider" />-->
|
||||||
|
<!-- <p class="listener-filed__title" key="validation-title">-->
|
||||||
|
<!-- <span><Icon icon="ep:menu" />约束条件列表:</span>-->
|
||||||
|
<!-- <el-button type="primary" @click="openFieldOptionForm(null, -1, 'constraint')"-->
|
||||||
|
<!-- >添加约束</el-button-->
|
||||||
|
<!-- >-->
|
||||||
|
<!-- </p>-->
|
||||||
|
<!-- <el-table :data="fieldConstraintsList" key="validation-table" max-height="240" fit border>-->
|
||||||
|
<!-- <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 #default="scope">-->
|
||||||
|
<!-- <el-button-->
|
||||||
|
<!-- type="primary"-->
|
||||||
|
<!-- link-->
|
||||||
|
<!-- @click="openFieldOptionForm(scope, scope.$index, 'constraint')"-->
|
||||||
|
<!-- >编辑</el-button-->
|
||||||
|
<!-- >-->
|
||||||
|
<!-- <el-divider direction="vertical" />-->
|
||||||
|
<!-- <el-button-->
|
||||||
|
<!-- type="primary"-->
|
||||||
|
<!-- link-->
|
||||||
|
<!-- style="color: #ff4d4f"-->
|
||||||
|
<!-- @click="removeFieldOptionItem(scope, scope.$index, 'constraint')"-->
|
||||||
|
<!-- >移除</el-button-->
|
||||||
|
<!-- >-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- </el-table-column>-->
|
||||||
|
<!-- </el-table>-->
|
||||||
|
|
||||||
|
<!-- <!– 表单属性 –>-->
|
||||||
|
<!-- <el-divider key="property-divider" />-->
|
||||||
|
<!-- <p class="listener-filed__title" key="property-title">-->
|
||||||
|
<!-- <span><Icon icon="ep:menu" />字段属性列表:</span>-->
|
||||||
|
<!-- <el-button type="primary" @click="openFieldOptionForm(null, -1, 'property')"-->
|
||||||
|
<!-- >添加属性</el-button-->
|
||||||
|
<!-- >-->
|
||||||
|
<!-- </p>-->
|
||||||
|
<!-- <el-table :data="fieldPropertiesList" key="property-table" max-height="240" fit border>-->
|
||||||
|
<!-- <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 #default="scope">-->
|
||||||
|
<!-- <el-button-->
|
||||||
|
<!-- type="primary"-->
|
||||||
|
<!-- link-->
|
||||||
|
<!-- @click="openFieldOptionForm(scope, scope.$index, 'property')"-->
|
||||||
|
<!-- >编辑</el-button-->
|
||||||
|
<!-- >-->
|
||||||
|
<!-- <el-divider direction="vertical" />-->
|
||||||
|
<!-- <el-button-->
|
||||||
|
<!-- type="primary"-->
|
||||||
|
<!-- link-->
|
||||||
|
<!-- style="color: #ff4d4f"-->
|
||||||
|
<!-- @click="removeFieldOptionItem(scope, scope.$index, 'property')"-->
|
||||||
|
<!-- >移除</el-button-->
|
||||||
|
<!-- >-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- </el-table-column>-->
|
||||||
|
<!-- </el-table>-->
|
||||||
|
|
||||||
|
<!-- <!– 底部按钮 –>-->
|
||||||
|
<!-- <div class="element-drawer__button">-->
|
||||||
|
<!-- <el-button>取 消</el-button>-->
|
||||||
|
<!-- <el-button 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" label-width="96px">-->
|
||||||
|
<!-- <el-form-item label="编号/ID" v-if="fieldOptionType !== 'constraint'" key="option-id">-->
|
||||||
|
<!-- <el-input v-model="fieldOptionForm.id" clearable />-->
|
||||||
|
<!-- </el-form-item>-->
|
||||||
|
<!-- <el-form-item label="名称" v-if="fieldOptionType !== 'property'" key="option-name">-->
|
||||||
|
<!-- <el-input v-model="fieldOptionForm.name" clearable />-->
|
||||||
|
<!-- </el-form-item>-->
|
||||||
|
<!-- <el-form-item label="配置" v-if="fieldOptionType === 'constraint'" key="option-config">-->
|
||||||
|
<!-- <el-input v-model="fieldOptionForm.config" clearable />-->
|
||||||
|
<!-- </el-form-item>-->
|
||||||
|
<!-- <el-form-item label="值" v-if="fieldOptionType === 'property'" key="option-value">-->
|
||||||
|
<!-- <el-input v-model="fieldOptionForm.value" clearable />-->
|
||||||
|
<!-- </el-form-item>-->
|
||||||
|
<!-- </el-form>-->
|
||||||
|
<!-- <template #footer>-->
|
||||||
|
<!-- <el-button @click="fieldOptionModelVisible = false">取 消</el-button>-->
|
||||||
|
<!-- <el-button type="primary" @click="saveFieldOption">确 定</el-button>-->
|
||||||
|
<!-- </template>-->
|
||||||
|
<!-- </el-dialog>-->
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, onMounted, inject,toRaw, watch,nextTick } from 'vue'
|
||||||
|
import * as FormApi from '@/api/bpm/form'
|
||||||
|
|
||||||
|
defineOptions({ name: 'ElementForm' })
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
id: String,
|
||||||
|
type: String
|
||||||
|
})
|
||||||
|
const prefix = inject('prefix')
|
||||||
|
const width = inject('width')
|
||||||
|
|
||||||
|
const formKey = ref('')
|
||||||
|
const businessKey = ref('')
|
||||||
|
const optionModelTitle = ref('')
|
||||||
|
const fieldList = ref<any[]>([])
|
||||||
|
const formFieldForm = ref<any>({})
|
||||||
|
const fieldType = ref({
|
||||||
|
long: '长整型',
|
||||||
|
string: '字符串',
|
||||||
|
boolean: '布尔类',
|
||||||
|
date: '日期类',
|
||||||
|
enum: '枚举类',
|
||||||
|
custom: '自定义类型'
|
||||||
|
})
|
||||||
|
const formFieldIndex = ref(-1) // 编辑中的字段, -1 为新增
|
||||||
|
const formFieldOptionIndex = ref(-1) // 编辑中的字段配置项, -1 为新增
|
||||||
|
const fieldModelVisible = ref(false)
|
||||||
|
const fieldOptionModelVisible = ref(false)
|
||||||
|
const fieldOptionForm = ref<any>({}) // 当前激活的字段配置项数据
|
||||||
|
const fieldOptionType = ref('') // 当前激活的字段配置项弹窗 类型
|
||||||
|
const fieldEnumList = ref<any[]>([]) // 枚举值列表
|
||||||
|
const fieldConstraintsList = ref<any[]>([]) // 约束条件列表
|
||||||
|
const fieldPropertiesList = ref<any[]>([]) // 绑定属性列表
|
||||||
|
const bpmnELement = ref()
|
||||||
|
const elExtensionElements = ref()
|
||||||
|
const formData = ref()
|
||||||
|
const otherExtensions = ref()
|
||||||
|
|
||||||
|
const bpmnInstances = () => (window as any)?.bpmnInstances
|
||||||
|
const resetFormList = () => {
|
||||||
|
bpmnELement.value = bpmnInstances().bpmnElement
|
||||||
|
formKey.value = bpmnELement.value.businessObject.formKey
|
||||||
|
if (formKey.value?.length > 0) {
|
||||||
|
formKey.value = parseInt(formKey.value)
|
||||||
|
}
|
||||||
|
// 获取元素扩展属性 或者 创建扩展属性
|
||||||
|
elExtensionElements.value =
|
||||||
|
bpmnELement.value.businessObject.get('extensionElements') ||
|
||||||
|
bpmnInstances().moddle.create('bpmn:ExtensionElements', { values: [] })
|
||||||
|
// 获取元素表单配置 或者 创建新的表单配置
|
||||||
|
formData.value =
|
||||||
|
elExtensionElements.value.values.filter((ex) => ex.$type === `${prefix}:FormData`)?.[0] ||
|
||||||
|
bpmnInstances().moddle.create(`${prefix}:FormData`, { fields: [] })
|
||||||
|
|
||||||
|
// 业务标识 businessKey, 绑定在 formData 中
|
||||||
|
businessKey.value = formData.value.businessKey
|
||||||
|
|
||||||
|
// 保留剩余扩展元素,便于后面更新该元素对应属性
|
||||||
|
otherExtensions.value = elExtensionElements.value.values.filter(
|
||||||
|
(ex) => ex.$type !== `${prefix}:FormData`
|
||||||
|
)
|
||||||
|
|
||||||
|
// 复制原始值,填充表格
|
||||||
|
fieldList.value = JSON.parse(JSON.stringify(formData.value.fields || []))
|
||||||
|
|
||||||
|
// 更新元素扩展属性,避免后续报错
|
||||||
|
updateElementExtensions()
|
||||||
|
}
|
||||||
|
const updateElementFormKey = () => {
|
||||||
|
bpmnInstances().modeling.updateProperties(toRaw(bpmnELement.value), {
|
||||||
|
formKey: formKey.value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const updateElementBusinessKey = () => {
|
||||||
|
bpmnInstances().modeling.updateModdleProperties(toRaw(bpmnELement.value), formData.value, {
|
||||||
|
businessKey: businessKey.value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 根据类型调整字段type
|
||||||
|
const changeFieldTypeType = (type) => {
|
||||||
|
// this.$set(this.formFieldForm, "type", type === "custom" ? "" : type);
|
||||||
|
formFieldForm.value['type'] = type === 'custom' ? '' : type
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开字段详情侧边栏
|
||||||
|
const openFieldForm = (field, index) => {
|
||||||
|
formFieldIndex.value = index
|
||||||
|
if (index !== -1) {
|
||||||
|
const FieldObject = formData.value.fields[index]
|
||||||
|
formFieldForm.value = JSON.parse(JSON.stringify(field))
|
||||||
|
// 设置自定义类型
|
||||||
|
// this.$set(this.formFieldForm, "typeType", !this.fieldType[field.type] ? "custom" : field.type);
|
||||||
|
formFieldForm.value['typeType'] = !fieldType.value[field.type] ? 'custom' : field.type
|
||||||
|
// 初始化枚举值列表
|
||||||
|
field.type === 'enum' &&
|
||||||
|
(fieldEnumList.value = JSON.parse(JSON.stringify(FieldObject?.values || [])))
|
||||||
|
// 初始化约束条件列表
|
||||||
|
fieldConstraintsList.value = JSON.parse(
|
||||||
|
JSON.stringify(FieldObject?.validation?.constraints || [])
|
||||||
|
)
|
||||||
|
// 初始化自定义属性列表
|
||||||
|
fieldPropertiesList.value = JSON.parse(JSON.stringify(FieldObject?.properties?.values || []))
|
||||||
|
} else {
|
||||||
|
formFieldForm.value = {}
|
||||||
|
// 初始化枚举值列表
|
||||||
|
fieldEnumList.value = []
|
||||||
|
// 初始化约束条件列表
|
||||||
|
fieldConstraintsList.value = []
|
||||||
|
// 初始化自定义属性列表
|
||||||
|
fieldPropertiesList.value = []
|
||||||
|
}
|
||||||
|
fieldModelVisible.value = true
|
||||||
|
}
|
||||||
|
// 打开字段 某个 配置项 弹窗
|
||||||
|
const openFieldOptionForm = (option, index, type) => {
|
||||||
|
fieldOptionModelVisible.value = true
|
||||||
|
fieldOptionType.value = type
|
||||||
|
formFieldOptionIndex.value = index
|
||||||
|
if (type === 'property') {
|
||||||
|
fieldOptionForm.value = option ? JSON.parse(JSON.stringify(option)) : {}
|
||||||
|
return (optionModelTitle.value = '属性配置')
|
||||||
|
}
|
||||||
|
if (type === 'enum') {
|
||||||
|
fieldOptionForm.value = option ? JSON.parse(JSON.stringify(option)) : {}
|
||||||
|
return (optionModelTitle.value = '枚举值配置')
|
||||||
|
}
|
||||||
|
fieldOptionForm.value = option ? JSON.parse(JSON.stringify(option)) : {}
|
||||||
|
return (optionModelTitle.value = '约束条件配置')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存字段 某个 配置项
|
||||||
|
const saveFieldOption = () => {
|
||||||
|
if (formFieldOptionIndex.value === -1) {
|
||||||
|
if (fieldOptionType.value === 'property') {
|
||||||
|
fieldPropertiesList.value.push(fieldOptionForm.value)
|
||||||
|
}
|
||||||
|
if (fieldOptionType.value === 'constraint') {
|
||||||
|
fieldConstraintsList.value.push(fieldOptionForm.value)
|
||||||
|
}
|
||||||
|
if (fieldOptionType.value === 'enum') {
|
||||||
|
fieldEnumList.value.push(fieldOptionForm.value)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fieldOptionType.value === 'property' &&
|
||||||
|
fieldPropertiesList.value.splice(formFieldOptionIndex.value, 1, fieldOptionForm.value)
|
||||||
|
fieldOptionType.value === 'constraint' &&
|
||||||
|
fieldConstraintsList.value.splice(formFieldOptionIndex.value, 1, fieldOptionForm.value)
|
||||||
|
fieldOptionType.value === 'enum' &&
|
||||||
|
fieldEnumList.value.splice(formFieldOptionIndex.value, 1, fieldOptionForm.value)
|
||||||
|
}
|
||||||
|
fieldOptionModelVisible.value = false
|
||||||
|
fieldOptionForm.value = {}
|
||||||
|
}
|
||||||
|
// 保存字段配置
|
||||||
|
const saveField = () => {
|
||||||
|
const { id, type, label, defaultValue, datePattern } = formFieldForm.value
|
||||||
|
const Field = bpmnInstances().moddle.create(`${prefix}:FormField`, { id, type, label })
|
||||||
|
defaultValue && (Field.defaultValue = defaultValue)
|
||||||
|
datePattern && (Field.datePattern = datePattern)
|
||||||
|
// 构建属性
|
||||||
|
if (fieldPropertiesList.value && fieldPropertiesList.value.length) {
|
||||||
|
const fieldPropertyList = fieldPropertiesList.value.map((fp) => {
|
||||||
|
return bpmnInstances().moddle.create(`${prefix}:Property`, {
|
||||||
|
id: fp.id,
|
||||||
|
value: fp.value
|
||||||
|
})
|
||||||
|
})
|
||||||
|
Field.properties = bpmnInstances().moddle.create(`${prefix}:Properties`, {
|
||||||
|
values: fieldPropertyList
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 构建校验规则
|
||||||
|
if (fieldConstraintsList.value && fieldConstraintsList.value.length) {
|
||||||
|
const fieldConstraintList = fieldConstraintsList.value.map((fc) => {
|
||||||
|
return bpmnInstances().moddle.create(`${prefix}:Constraint`, {
|
||||||
|
name: fc.name,
|
||||||
|
config: fc.config
|
||||||
|
})
|
||||||
|
})
|
||||||
|
Field.validation = bpmnInstances().moddle.create(`${prefix}:Validation`, {
|
||||||
|
constraints: fieldConstraintList
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 构建枚举值
|
||||||
|
if (fieldEnumList.value && fieldEnumList.value.length) {
|
||||||
|
Field.values = fieldEnumList.value.map((fe) => {
|
||||||
|
return bpmnInstances().moddle.create(`${prefix}:Value`, { name: fe.name, id: fe.id })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 更新数组 与 表单配置实例
|
||||||
|
if (formFieldIndex.value === -1) {
|
||||||
|
fieldList.value.push(formFieldForm.value)
|
||||||
|
formData.value.fields.push(Field)
|
||||||
|
} else {
|
||||||
|
fieldList.value.splice(formFieldIndex.value, 1, formFieldForm.value)
|
||||||
|
formData.value.fields.splice(formFieldIndex.value, 1, Field)
|
||||||
|
}
|
||||||
|
updateElementExtensions()
|
||||||
|
fieldModelVisible.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移除某个 字段的 配置项
|
||||||
|
const removeFieldOptionItem = (option, index, type) => {
|
||||||
|
// console.log(option, 'option')
|
||||||
|
if (type === 'property') {
|
||||||
|
fieldPropertiesList.value.splice(index, 1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (type === 'enum') {
|
||||||
|
fieldEnumList.value.splice(index, 1)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fieldConstraintsList.value.splice(index, 1)
|
||||||
|
}
|
||||||
|
// 移除 字段
|
||||||
|
const removeField = (field, index) => {
|
||||||
|
console.log(field, 'field')
|
||||||
|
fieldList.value.splice(index, 1)
|
||||||
|
formData.value.fields.splice(index, 1)
|
||||||
|
updateElementExtensions()
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateElementExtensions = () => {
|
||||||
|
// 更新回扩展元素
|
||||||
|
const newElExtensionElements = bpmnInstances().moddle.create(`bpmn:ExtensionElements`, {
|
||||||
|
values: otherExtensions.value.concat(formData.value)
|
||||||
|
})
|
||||||
|
// 更新到元素上
|
||||||
|
bpmnInstances().modeling.updateProperties(toRaw(bpmnELement.value), {
|
||||||
|
extensionElements: newElExtensionElements
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const formList = ref([]) // 流程表单的下拉框的数据
|
||||||
|
onMounted(async () => {
|
||||||
|
formList.value = await FormApi.getFormSimpleList()
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.id,
|
||||||
|
(val) => {
|
||||||
|
val &&
|
||||||
|
val.length &&
|
||||||
|
nextTick(() => {
|
||||||
|
resetFormList()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import MyPropertiesPanel from './PropertiesPanel.vue'
|
||||||
|
|
||||||
|
MyPropertiesPanel.install = function (Vue) {
|
||||||
|
Vue.component(MyPropertiesPanel.name, MyPropertiesPanel)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MyPropertiesPanel
|
||||||
@@ -0,0 +1,401 @@
|
|||||||
|
<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="100px">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button size="small" link @click="openListenerForm(scope.row, scope.$index)">编辑</el-button>
|
||||||
|
<el-divider direction="vertical" />
|
||||||
|
<el-button size="small" link style="color: #ff4d4f" @click="removeListener(scope.$index)">
|
||||||
|
移除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div class="element-drawer__button">
|
||||||
|
<el-button type="primary" icon="el-icon-Plus" size="small" @click="openListenerForm(null)">
|
||||||
|
添加监听器
|
||||||
|
</el-button>
|
||||||
|
<el-button type="success" icon="el-icon-Select" size="small" @click="openProcessListenerDialog">
|
||||||
|
选择监听器
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 监听器 编辑/创建 部分 -->
|
||||||
|
<el-drawer
|
||||||
|
v-model="listenerFormModelVisible"
|
||||||
|
title="执行监听器"
|
||||||
|
:size="`${width}px`"
|
||||||
|
append-to-body
|
||||||
|
destroy-on-close
|
||||||
|
>
|
||||||
|
<el-form :model="listenerForm" label-width="96px" ref="listenerFormRef">
|
||||||
|
<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="代理表达式"
|
||||||
|
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>
|
||||||
|
<Icon icon="ep:menu" />
|
||||||
|
注入字段:
|
||||||
|
</span>
|
||||||
|
<el-button type="primary" @click="openListenerFieldForm(null)">添加字段</el-button>
|
||||||
|
</p>
|
||||||
|
<el-table :data="fieldsListOfListener" size="small" max-height="240" fit border 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="130px">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button size="small" link @click="openListenerFieldForm(scope.row, scope.$index)">
|
||||||
|
编辑
|
||||||
|
</el-button>
|
||||||
|
<el-divider direction="vertical" />
|
||||||
|
<el-button size="small" link style="color: #ff4d4f" @click="removeListenerField(scope.$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" label-width="96spx" ref="listenerFieldFormRef" style="height: 136px">
|
||||||
|
<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 #footer>
|
||||||
|
<el-button size="small" @click="listenerFieldFormModelVisible = false">取 消</el-button>
|
||||||
|
<el-button size="small" type="primary" @click="saveListenerFiled">确 定</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 选择弹窗 -->
|
||||||
|
<ProcessListenerDialog ref="processListenerDialogRef" @select="selectProcessListener" />
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, watch, nextTick, inject } from 'vue'
|
||||||
|
import { ElMessageBox } from 'element-plus'
|
||||||
|
import { createListenerObject, updateElementExtensions } from '../../utils'
|
||||||
|
import { initListenerType, initListenerForm, listenerType, fieldType, initListenerForm2 } from './utilSelf'
|
||||||
|
import ProcessListenerDialog from './ProcessListenerDialog.vue'
|
||||||
|
|
||||||
|
defineOptions({ name: 'ElementListeners' })
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
id: String,
|
||||||
|
type: String
|
||||||
|
})
|
||||||
|
const prefix = inject('prefix')
|
||||||
|
const width = inject('width')
|
||||||
|
const elementListenersList = ref<any[]>([]) // 监听器列表
|
||||||
|
const listenerForm = ref<any>({}) // 监听器详情表单
|
||||||
|
const listenerFormModelVisible = ref(false) // 监听器 编辑 侧边栏显示状态
|
||||||
|
const fieldsListOfListener = ref<any[]>([])
|
||||||
|
const listenerFieldForm = ref<any>({}) // 监听器 注入字段 详情表单
|
||||||
|
const listenerFieldFormModelVisible = ref(false) // 监听器 注入字段表单弹窗 显示状态
|
||||||
|
const editingListenerIndex = ref(-1) // 监听器所在下标,-1 为新增
|
||||||
|
const editingListenerFieldIndex = ref(-1) // 字段所在下标,-1 为新增
|
||||||
|
const listenerTypeObject = ref(listenerType)
|
||||||
|
const fieldTypeObject = ref(fieldType)
|
||||||
|
const bpmnElement = ref()
|
||||||
|
const otherExtensionList = ref()
|
||||||
|
const bpmnElementListeners = ref()
|
||||||
|
const listenerFormRef = ref()
|
||||||
|
const listenerFieldFormRef = ref()
|
||||||
|
const bpmnInstances = () => (window as any)?.bpmnInstances
|
||||||
|
|
||||||
|
const resetListenersList = () => {
|
||||||
|
bpmnElement.value = bpmnInstances().bpmnElement
|
||||||
|
otherExtensionList.value = []
|
||||||
|
bpmnElementListeners.value =
|
||||||
|
bpmnElement.value.businessObject?.extensionElements?.values?.filter(
|
||||||
|
ex => ex.$type === `${prefix}:ExecutionListener`
|
||||||
|
) ?? []
|
||||||
|
elementListenersList.value = bpmnElementListeners.value.map(listener => initListenerType(listener))
|
||||||
|
}
|
||||||
|
// 打开 监听器详情 侧边栏
|
||||||
|
const openListenerForm = (listener, index?) => {
|
||||||
|
// debugger
|
||||||
|
if (listener) {
|
||||||
|
listenerForm.value = initListenerForm(listener)
|
||||||
|
editingListenerIndex.value = index
|
||||||
|
} else {
|
||||||
|
listenerForm.value = {}
|
||||||
|
editingListenerIndex.value = -1 // 标记为新增
|
||||||
|
}
|
||||||
|
if (listener && listener.fields) {
|
||||||
|
fieldsListOfListener.value = listener.fields.map(field => ({
|
||||||
|
...field,
|
||||||
|
fieldType: field.string ? 'string' : 'expression'
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
fieldsListOfListener.value = []
|
||||||
|
listenerForm.value['fields'] = []
|
||||||
|
}
|
||||||
|
// 打开侧边栏并清楚验证状态
|
||||||
|
listenerFormModelVisible.value = true
|
||||||
|
nextTick(() => {
|
||||||
|
if (listenerFormRef.value) {
|
||||||
|
listenerFormRef.value.clearValidate()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 打开监听器字段编辑弹窗
|
||||||
|
const openListenerFieldForm = (field, index?) => {
|
||||||
|
listenerFieldForm.value = field ? JSON.parse(JSON.stringify(field)) : {}
|
||||||
|
editingListenerFieldIndex.value = field ? index : -1
|
||||||
|
listenerFieldFormModelVisible.value = true
|
||||||
|
nextTick(() => {
|
||||||
|
if (listenerFieldFormRef.value) {
|
||||||
|
listenerFieldFormRef.value.clearValidate()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 保存监听器注入字段
|
||||||
|
const saveListenerFiled = async () => {
|
||||||
|
// debugger
|
||||||
|
let validateStatus = await listenerFieldFormRef.value.validate()
|
||||||
|
if (!validateStatus) return // 验证不通过直接返回
|
||||||
|
if (editingListenerFieldIndex.value === -1) {
|
||||||
|
fieldsListOfListener.value.push(listenerFieldForm.value)
|
||||||
|
listenerForm.value.fields.push(listenerFieldForm.value)
|
||||||
|
} else {
|
||||||
|
fieldsListOfListener.value.splice(editingListenerFieldIndex.value, 1, listenerFieldForm.value)
|
||||||
|
listenerForm.value.fields.splice(editingListenerFieldIndex.value, 1, listenerFieldForm.value)
|
||||||
|
}
|
||||||
|
listenerFieldFormModelVisible.value = false
|
||||||
|
nextTick(() => {
|
||||||
|
listenerFieldForm.value = {}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 移除监听器字段
|
||||||
|
const removeListenerField = index => {
|
||||||
|
// debugger
|
||||||
|
ElMessageBox.confirm('确认移除该字段吗?', '提示', {
|
||||||
|
confirmButtonText: '确 认',
|
||||||
|
cancelButtonText: '取 消'
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
fieldsListOfListener.value.splice(index, 1)
|
||||||
|
listenerForm.value.fields.splice(index, 1)
|
||||||
|
})
|
||||||
|
.catch(() => console.info('操作取消'))
|
||||||
|
}
|
||||||
|
// 移除监听器
|
||||||
|
const removeListener = index => {
|
||||||
|
debugger
|
||||||
|
ElMessageBox.confirm('确认移除该监听器吗?', '提示', {
|
||||||
|
confirmButtonText: '确 认',
|
||||||
|
cancelButtonText: '取 消'
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
bpmnElementListeners.value.splice(index, 1)
|
||||||
|
elementListenersList.value.splice(index, 1)
|
||||||
|
updateElementExtensions(bpmnElement.value, otherExtensionList.value.concat(bpmnElementListeners.value))
|
||||||
|
})
|
||||||
|
.catch(() => console.info('操作取消'))
|
||||||
|
}
|
||||||
|
// 保存监听器配置
|
||||||
|
const saveListenerConfig = async () => {
|
||||||
|
// debugger
|
||||||
|
let validateStatus = await listenerFormRef.value.validate()
|
||||||
|
if (!validateStatus) return // 验证不通过直接返回
|
||||||
|
const listenerObject = createListenerObject(listenerForm.value, false, prefix)
|
||||||
|
if (editingListenerIndex.value === -1) {
|
||||||
|
bpmnElementListeners.value.push(listenerObject)
|
||||||
|
elementListenersList.value.push(listenerForm.value)
|
||||||
|
} else {
|
||||||
|
bpmnElementListeners.value.splice(editingListenerIndex.value, 1, listenerObject)
|
||||||
|
elementListenersList.value.splice(editingListenerIndex.value, 1, listenerForm.value)
|
||||||
|
}
|
||||||
|
// 保存其他配置
|
||||||
|
otherExtensionList.value =
|
||||||
|
bpmnElement.value.businessObject?.extensionElements?.values?.filter(
|
||||||
|
ex => ex.$type !== `${prefix}:ExecutionListener`
|
||||||
|
) ?? []
|
||||||
|
updateElementExtensions(bpmnElement.value, otherExtensionList.value.concat(bpmnElementListeners.value))
|
||||||
|
// 4. 隐藏侧边栏
|
||||||
|
listenerFormModelVisible.value = false
|
||||||
|
listenerForm.value = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开监听器弹窗
|
||||||
|
const processListenerDialogRef = ref()
|
||||||
|
const openProcessListenerDialog = async () => {
|
||||||
|
processListenerDialogRef.value.open('execution')
|
||||||
|
}
|
||||||
|
const selectProcessListener = listener => {
|
||||||
|
const listenerForm = initListenerForm2(listener)
|
||||||
|
const listenerObject = createListenerObject(listenerForm, false, prefix)
|
||||||
|
bpmnElementListeners.value.push(listenerObject)
|
||||||
|
elementListenersList.value.push(listenerForm)
|
||||||
|
|
||||||
|
// 保存其他配置
|
||||||
|
otherExtensionList.value =
|
||||||
|
bpmnElement.value.businessObject?.extensionElements?.values?.filter(
|
||||||
|
ex => ex.$type !== `${prefix}:ExecutionListener`
|
||||||
|
) ?? []
|
||||||
|
updateElementExtensions(bpmnElement.value, otherExtensionList.value.concat(bpmnElementListeners.value))
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.id,
|
||||||
|
val => {
|
||||||
|
val &&
|
||||||
|
val.length &&
|
||||||
|
nextTick(() => {
|
||||||
|
resetListenersList()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
<!-- 执行器选择 -->
|
||||||
|
<template>
|
||||||
|
<Dialog title="请选择监听器" v-model="dialogVisible" width="1024px">
|
||||||
|
<ContentWrap>
|
||||||
|
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||||
|
<el-table-column label="名字" align="center" prop="name" />
|
||||||
|
<el-table-column label="类型" align="center" prop="type">
|
||||||
|
<template #default="scope">
|
||||||
|
<!-- <dict-tag :type="DICT_TYPE.BPM_PROCESS_LISTENER_TYPE" :value="scope.row.type" /> -->
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="事件" align="center" prop="event" />
|
||||||
|
<el-table-column label="值类型" align="center" prop="valueType">
|
||||||
|
<template #default="scope">
|
||||||
|
<!-- <dict-tag
|
||||||
|
:type="DICT_TYPE.BPM_PROCESS_LISTENER_VALUE_TYPE"
|
||||||
|
:value="scope.row.valueType"
|
||||||
|
/> -->
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="值" align="center" prop="value" />
|
||||||
|
<el-table-column label="操作" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button link type="primary" @click="select(scope.row)">选择</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<!-- 分页 -->
|
||||||
|
<Pagination
|
||||||
|
:total="total"
|
||||||
|
v-model:page="queryParams.pageNo"
|
||||||
|
v-model:limit="queryParams.pageSize"
|
||||||
|
@pagination="getList"
|
||||||
|
/>
|
||||||
|
</ContentWrap>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { onMounted, reactive, ref, watch, onBeforeUnmount } from 'vue'
|
||||||
|
// import { ProcessListenerApi, ProcessListenerVO } from '@/api/bpm/processListener'
|
||||||
|
// import { DICT_TYPE } from '@/utils/dict'
|
||||||
|
// import { CommonStatusEnum } from '@/utils/constants'
|
||||||
|
|
||||||
|
/** BPM 流程 表单 */
|
||||||
|
defineOptions({ name: 'ProcessListenerDialog' })
|
||||||
|
|
||||||
|
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||||
|
const loading = ref(true) // 列表的加载中
|
||||||
|
const list = ref([]) // 列表的数据
|
||||||
|
const total = ref(0) // 列表的总页数
|
||||||
|
const queryParams = reactive({
|
||||||
|
pageNo: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
type: undefined
|
||||||
|
// status: CommonStatusEnum.ENABLE
|
||||||
|
})
|
||||||
|
|
||||||
|
/** 打开弹窗 */
|
||||||
|
const open = async (type: string) => {
|
||||||
|
dialogVisible.value = true
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
queryParams.pageNo = 1
|
||||||
|
queryParams.type = type
|
||||||
|
// const data = await ProcessListenerApi.getProcessListenerPage(queryParams)
|
||||||
|
// list.value = data.list
|
||||||
|
// total.value = data.total
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||||
|
|
||||||
|
/** 提交表单 */
|
||||||
|
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||||
|
const select = async row => {
|
||||||
|
dialogVisible.value = false
|
||||||
|
// 发送操作成功的事件
|
||||||
|
emit('select', row)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,448 @@
|
|||||||
|
<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="row => listenerEventTypeObject[row.event]"
|
||||||
|
/>
|
||||||
|
<el-table-column label="事件id" min-width="80px" prop="id" show-overflow-tooltip />
|
||||||
|
<el-table-column
|
||||||
|
label="监听器类型"
|
||||||
|
min-width="80px"
|
||||||
|
show-overflow-tooltip
|
||||||
|
:formatter="row => listenerTypeObject[row.listenerType]"
|
||||||
|
/>
|
||||||
|
<el-table-column label="操作" width="90px">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button size="small" link @click="openListenerForm(scope.row, scope.$index)">编辑</el-button>
|
||||||
|
<el-divider direction="vertical" />
|
||||||
|
<el-button
|
||||||
|
size="small"
|
||||||
|
link
|
||||||
|
style="color: #ff4d4f"
|
||||||
|
@click="removeListener(scope.row, scope.$index)"
|
||||||
|
>
|
||||||
|
移除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div class="element-drawer__button">
|
||||||
|
<el-Button size="small" type="primary" icon="el-icon-Plus" @click="openListenerForm(null)">
|
||||||
|
添加监听器
|
||||||
|
</el-Button>
|
||||||
|
<el-button type="success" icon="el-icon-Select" size="small" @click="openProcessListenerDialog">
|
||||||
|
选择监听器
|
||||||
|
</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">
|
||||||
|
<el-form-item label="事件类型" prop="event" :rules="{ required: true, trigger: ['blur', 'change'] }">
|
||||||
|
<el-select v-model="listenerForm.event">
|
||||||
|
<el-option
|
||||||
|
v-for="i in Object.keys(listenerEventTypeObject)"
|
||||||
|
:key="i"
|
||||||
|
:label="listenerEventTypeObject[i]"
|
||||||
|
:value="i"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="监听器ID" prop="id" :rules="{ required: true, trigger: ['blur', 'change'] }">
|
||||||
|
<el-input v-model="listenerForm.id" clearable />
|
||||||
|
</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="代理表达式"
|
||||||
|
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>
|
||||||
|
|
||||||
|
<template v-if="listenerForm.event === 'timeout'">
|
||||||
|
<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="null" />
|
||||||
|
</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 />
|
||||||
|
<p class="listener-filed__title">
|
||||||
|
<span>
|
||||||
|
<Icon icon="ep:menu" />
|
||||||
|
注入字段:
|
||||||
|
</span>
|
||||||
|
<el-button size="small" type="primary" @click="openListenerFieldForm(null)">添加字段</el-button>
|
||||||
|
</p>
|
||||||
|
<el-table :data="fieldsListOfListener" size="small" max-height="240" fit border 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 #default="scope">
|
||||||
|
<el-button size="small" link @click="openListenerFieldForm(scope.row, scope.$index)">
|
||||||
|
编辑
|
||||||
|
</el-button>
|
||||||
|
<el-divider direction="vertical" />
|
||||||
|
<el-button
|
||||||
|
size="small"
|
||||||
|
link
|
||||||
|
style="color: #ff4d4f"
|
||||||
|
@click="removeListenerField(scope.row, scope.$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"
|
||||||
|
>
|
||||||
|
<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 #footer>
|
||||||
|
<el-button size="small" @click="listenerFieldFormModelVisible = false">取 消</el-button>
|
||||||
|
<el-button size="small" type="primary" @click="saveListenerFiled">确 定</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 选择弹窗 -->
|
||||||
|
<!-- <ProcessListenerDialog ref="processListenerDialogRef" @select="selectProcessListener" /> -->
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { onMounted, provide, ref, watch, nextTick, inject } from 'vue'
|
||||||
|
import { ElMessageBox } from 'element-plus'
|
||||||
|
import { createListenerObject, updateElementExtensions } from '../../utils'
|
||||||
|
import { initListenerForm, initListenerType, eventType, listenerType, fieldType, initListenerForm2 } from './utilSelf'
|
||||||
|
// import ProcessListenerDialog from '@/components/bpmnProcessDesigner/package/penal/listeners/ProcessListenerDialog.vue'
|
||||||
|
|
||||||
|
defineOptions({ name: 'UserTaskListeners' })
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
id: String,
|
||||||
|
type: String
|
||||||
|
})
|
||||||
|
const prefix = inject('prefix')
|
||||||
|
const width = inject('width')
|
||||||
|
const elementListenersList = ref<any[]>([])
|
||||||
|
const listenerEventTypeObject = ref(eventType)
|
||||||
|
const listenerTypeObject = ref(listenerType)
|
||||||
|
const listenerFormModelVisible = ref(false)
|
||||||
|
const listenerForm = ref<any>({})
|
||||||
|
const fieldTypeObject = ref(fieldType)
|
||||||
|
const fieldsListOfListener = ref<any[]>([])
|
||||||
|
const listenerFieldFormModelVisible = ref(false) // 监听器 注入字段表单弹窗 显示状态
|
||||||
|
const editingListenerIndex = ref(-1) // 监听器所在下标,-1 为新增
|
||||||
|
const editingListenerFieldIndex = ref(-1) // 字段所在下标,-1 为新增
|
||||||
|
const listenerFieldForm = ref<any>({}) // 监听器 注入字段 详情表单
|
||||||
|
const bpmnElement = ref()
|
||||||
|
const bpmnElementListeners = ref()
|
||||||
|
const otherExtensionList = ref()
|
||||||
|
const listenerFormRef = ref()
|
||||||
|
const listenerFieldFormRef = ref()
|
||||||
|
const bpmnInstances = () => (window as any)?.bpmnInstances
|
||||||
|
|
||||||
|
const resetListenersList = () => {
|
||||||
|
console.log(
|
||||||
|
bpmnInstances().bpmnElement,
|
||||||
|
'window.bpmnInstances.bpmnElementwindow.bpmnInstances.bpmnElementwindow.bpmnInstances.bpmnElementwindow.bpmnInstances.bpmnElementwindow.bpmnInstances.bpmnElementwindow.bpmnInstances.bpmnElement'
|
||||||
|
)
|
||||||
|
bpmnElement.value = bpmnInstances().bpmnElement
|
||||||
|
otherExtensionList.value = []
|
||||||
|
bpmnElementListeners.value =
|
||||||
|
bpmnElement.value.businessObject?.extensionElements?.values.filter(
|
||||||
|
ex => ex.$type === `${prefix}:TaskListener`
|
||||||
|
) ?? []
|
||||||
|
elementListenersList.value = bpmnElementListeners.value.map(listener => initListenerType(listener))
|
||||||
|
}
|
||||||
|
const openListenerForm = (listener, index?) => {
|
||||||
|
if (listener) {
|
||||||
|
listenerForm.value = initListenerForm(listener)
|
||||||
|
editingListenerIndex.value = index
|
||||||
|
} else {
|
||||||
|
listenerForm.value = {}
|
||||||
|
editingListenerIndex.value = -1 // 标记为新增
|
||||||
|
}
|
||||||
|
if (listener && listener.fields) {
|
||||||
|
fieldsListOfListener.value = listener.fields.map(field => ({
|
||||||
|
...field,
|
||||||
|
fieldType: field.string ? 'string' : 'expression'
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
fieldsListOfListener.value = []
|
||||||
|
listenerForm.value['fields'] = []
|
||||||
|
}
|
||||||
|
// 打开侧边栏并清楚验证状态
|
||||||
|
listenerFormModelVisible.value = true
|
||||||
|
nextTick(() => {
|
||||||
|
if (listenerFormRef.value) listenerFormRef.value.clearValidate()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 移除监听器
|
||||||
|
const removeListener = (listener, index?) => {
|
||||||
|
console.log(listener, 'listener')
|
||||||
|
ElMessageBox.confirm('确认移除该监听器吗?', '提示', {
|
||||||
|
confirmButtonText: '确 认',
|
||||||
|
cancelButtonText: '取 消'
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
bpmnElementListeners.value.splice(index, 1)
|
||||||
|
elementListenersList.value.splice(index, 1)
|
||||||
|
updateElementExtensions(bpmnElement.value, otherExtensionList.value.concat(bpmnElementListeners.value))
|
||||||
|
})
|
||||||
|
.catch(() => console.info('操作取消'))
|
||||||
|
}
|
||||||
|
// 保存监听器
|
||||||
|
const saveListenerConfig = async () => {
|
||||||
|
let validateStatus = await listenerFormRef.value.validate()
|
||||||
|
if (!validateStatus) return // 验证不通过直接返回
|
||||||
|
const listenerObject = createListenerObject(listenerForm.value, true, prefix)
|
||||||
|
if (editingListenerIndex.value === -1) {
|
||||||
|
bpmnElementListeners.value.push(listenerObject)
|
||||||
|
elementListenersList.value.push(listenerForm.value)
|
||||||
|
} else {
|
||||||
|
bpmnElementListeners.value.splice(editingListenerIndex.value, 1, listenerObject)
|
||||||
|
elementListenersList.value.splice(editingListenerIndex.value, 1, listenerForm.value)
|
||||||
|
}
|
||||||
|
// 保存其他配置
|
||||||
|
otherExtensionList.value =
|
||||||
|
bpmnElement.value.businessObject?.extensionElements?.values?.filter(
|
||||||
|
ex => ex.$type !== `${prefix}:TaskListener`
|
||||||
|
) ?? []
|
||||||
|
updateElementExtensions(bpmnElement.value, otherExtensionList.value.concat(bpmnElementListeners.value))
|
||||||
|
// 4. 隐藏侧边栏
|
||||||
|
listenerFormModelVisible.value = false
|
||||||
|
listenerForm.value = {}
|
||||||
|
}
|
||||||
|
// 打开监听器字段编辑弹窗
|
||||||
|
const openListenerFieldForm = (field, index?) => {
|
||||||
|
listenerFieldForm.value = field ? JSON.parse(JSON.stringify(field)) : {}
|
||||||
|
editingListenerFieldIndex.value = field ? index : -1
|
||||||
|
listenerFieldFormModelVisible.value = true
|
||||||
|
nextTick(() => {
|
||||||
|
if (listenerFieldFormRef.value) listenerFieldFormRef.value.clearValidate()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 保存监听器注入字段
|
||||||
|
const saveListenerFiled = async () => {
|
||||||
|
let validateStatus = await listenerFieldFormRef.value.validate()
|
||||||
|
if (!validateStatus) return // 验证不通过直接返回
|
||||||
|
if (editingListenerFieldIndex.value === -1) {
|
||||||
|
fieldsListOfListener.value.push(listenerFieldForm.value)
|
||||||
|
listenerForm.value.fields.push(listenerFieldForm.value)
|
||||||
|
} else {
|
||||||
|
fieldsListOfListener.value.splice(editingListenerFieldIndex.value, 1, listenerFieldForm.value)
|
||||||
|
listenerForm.value.fields.splice(editingListenerFieldIndex.value, 1, listenerFieldForm.value)
|
||||||
|
}
|
||||||
|
listenerFieldFormModelVisible.value = false
|
||||||
|
nextTick(() => {
|
||||||
|
listenerFieldForm.value = {}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 移除监听器字段
|
||||||
|
const removeListenerField = (field, index) => {
|
||||||
|
console.log(field, 'field')
|
||||||
|
ElMessageBox.confirm('确认移除该字段吗?', '提示', {
|
||||||
|
confirmButtonText: '确 认',
|
||||||
|
cancelButtonText: '取 消'
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
fieldsListOfListener.value.splice(index, 1)
|
||||||
|
listenerForm.value.fields.splice(index, 1)
|
||||||
|
})
|
||||||
|
.catch(() => console.info('操作取消'))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开监听器弹窗
|
||||||
|
const processListenerDialogRef = ref()
|
||||||
|
const openProcessListenerDialog = async () => {
|
||||||
|
processListenerDialogRef.value.open('task')
|
||||||
|
}
|
||||||
|
const selectProcessListener = listener => {
|
||||||
|
const listenerForm = initListenerForm2(listener)
|
||||||
|
const listenerObject = createListenerObject(listenerForm, true, prefix)
|
||||||
|
bpmnElementListeners.value.push(listenerObject)
|
||||||
|
elementListenersList.value.push(listenerForm)
|
||||||
|
|
||||||
|
// 保存其他配置
|
||||||
|
otherExtensionList.value =
|
||||||
|
bpmnElement.value.businessObject?.extensionElements?.values?.filter(
|
||||||
|
ex => ex.$type !== `${prefix}:TaskListener`
|
||||||
|
) ?? []
|
||||||
|
updateElementExtensions(bpmnElement.value, otherExtensionList.value.concat(bpmnElementListeners.value))
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.id,
|
||||||
|
val => {
|
||||||
|
val &&
|
||||||
|
val.length &&
|
||||||
|
nextTick(() => {
|
||||||
|
resetListenersList()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
</script>
|
||||||
@@ -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 #default="scope">
|
||||||
|
<el-button size="small" type="primary" link @click="openListenerForm(scope, scope.$index)">编辑</el-button>
|
||||||
|
<el-divider direction="vertical" />
|
||||||
|
<el-button size="small" type="primary" link style="color: #ff4d4f" @click="removeListener(scope, scope.$index)">移除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div class="element-drawer__button">
|
||||||
|
<el-button size="small" type="primary" icon="el-icon-plus" @click="openListenerForm(null)">添加监听器</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 监听器 编辑/创建 部分 -->
|
||||||
|
<el-drawer :visible.sync="listenerFormModelVisible" title="执行监听器" :size="width + 'px'" append-to-body destroy-on-close>
|
||||||
|
<el-form size="small" :model="listenerForm" label-width="96px" ref="listenerFormRef" @submit.native.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="代理表达式"
|
||||||
|
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><i class="el-icon-menu"></i>注入字段:</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 #default="scope">
|
||||||
|
<el-button size="small" type="primary" link @click="openListenerFieldForm(scope, scope.$index)">编辑</el-button>
|
||||||
|
<el-divider direction="vertical" />
|
||||||
|
<el-button size="small" type="primary" link style="color: #ff4d4f" @click="removeListenerField(scope, scope.$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="字段配置" :visible.sync="listenerFieldFormModelVisible" width="600px" append-to-body destroy-on-close>
|
||||||
|
<el-form :model="listenerFieldForm" size="small" label-width="96px" ref="listenerFieldFormRef" style="height: 136px" @submit.native.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 #footer>
|
||||||
|
<el-button size="small" @click="listenerFieldFormModelVisible = false">取 消</el-button>
|
||||||
|
<el-button size="small" type="primary" @click="saveListenerFiled">确 定</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
// 初始化表单数据
|
||||||
|
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 (const 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 将 ProcessListenerDO 转换成 initListenerForm 想同的 Form 对象 */
|
||||||
|
export function initListenerForm2(processListener) {
|
||||||
|
if (processListener.valueType === 'class') {
|
||||||
|
return {
|
||||||
|
listenerType: 'classListener',
|
||||||
|
class: processListener.value,
|
||||||
|
event: processListener.event,
|
||||||
|
fields: []
|
||||||
|
}
|
||||||
|
} else if (processListener.valueType === 'expression') {
|
||||||
|
return {
|
||||||
|
listenerType: 'expressionListener',
|
||||||
|
expression: processListener.value,
|
||||||
|
event: processListener.event,
|
||||||
|
fields: []
|
||||||
|
}
|
||||||
|
} else if (processListener.valueType === 'delegateExpression') {
|
||||||
|
return {
|
||||||
|
listenerType: 'delegateExpressionListener',
|
||||||
|
delegateExpression: processListener.value,
|
||||||
|
event: processListener.event,
|
||||||
|
fields: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error('未知的监听器类型')
|
||||||
|
}
|
||||||
|
|
||||||
|
export const listenerType = {
|
||||||
|
classListener: 'Java 类',
|
||||||
|
expressionListener: '表达式',
|
||||||
|
delegateExpressionListener: '代理表达式',
|
||||||
|
scriptListener: '脚本'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const eventType = {
|
||||||
|
create: '创建',
|
||||||
|
assignment: '指派',
|
||||||
|
complete: '完成',
|
||||||
|
delete: '删除',
|
||||||
|
update: '更新',
|
||||||
|
timeout: '超时'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const fieldType = {
|
||||||
|
string: '字符串',
|
||||||
|
expression: '表达式'
|
||||||
|
}
|
||||||
@@ -0,0 +1,281 @@
|
|||||||
|
<template>
|
||||||
|
<div class="panel-tab__content">
|
||||||
|
<el-form label-width="90px">
|
||||||
|
<el-form-item label="快捷配置">
|
||||||
|
<el-button size="small" @click="changeConfig('依次审批')">依次审批</el-button>
|
||||||
|
<el-button size="small" @click="changeConfig('会签')">会签</el-button>
|
||||||
|
<el-button size="small" @click="changeConfig('或签')">或签</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="会签类型">
|
||||||
|
<el-select v-model="loopCharacteristics" @change="changeLoopCharacteristicsType">
|
||||||
|
<el-option label="并行多重事件" value="ParallelMultiInstance" />
|
||||||
|
<el-option label="时序多重事件" value="SequentialMultiInstance" />
|
||||||
|
<el-option label="无" value="Null" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<template
|
||||||
|
v-if="
|
||||||
|
loopCharacteristics === 'ParallelMultiInstance' ||
|
||||||
|
loopCharacteristics === 'SequentialMultiInstance'
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<el-form-item label="循环数量" key="loopCardinality">
|
||||||
|
<el-input
|
||||||
|
v-model="loopInstanceForm.loopCardinality"
|
||||||
|
clearable
|
||||||
|
@change="updateLoopCardinality"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="集合" key="collection" v-show="false">
|
||||||
|
<el-input v-model="loopInstanceForm.collection" clearable @change="updateLoopBase" />
|
||||||
|
</el-form-item>
|
||||||
|
<!-- add by 芋艿:由于「元素变量」暂时用不到,所以这里 display 为 none -->
|
||||||
|
<el-form-item label="元素变量" key="elementVariable" style="display: none">
|
||||||
|
<el-input v-model="loopInstanceForm.elementVariable" clearable @change="updateLoopBase" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="完成条件" key="completionCondition">
|
||||||
|
<el-input
|
||||||
|
v-model="loopInstanceForm.completionCondition"
|
||||||
|
clearable
|
||||||
|
@change="updateLoopCondition"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<!-- add by 芋艿:由于「异步状态」暂时用不到,所以这里 display 为 none -->
|
||||||
|
<el-form-item label="异步状态" key="async" style="display: none">
|
||||||
|
<el-checkbox
|
||||||
|
v-model="loopInstanceForm.asyncBefore"
|
||||||
|
label="异步前"
|
||||||
|
@change="updateLoopAsync('asyncBefore')"
|
||||||
|
/>
|
||||||
|
<el-checkbox
|
||||||
|
v-model="loopInstanceForm.asyncAfter"
|
||||||
|
label="异步后"
|
||||||
|
@change="updateLoopAsync('asyncAfter')"
|
||||||
|
/>
|
||||||
|
<el-checkbox
|
||||||
|
v-model="loopInstanceForm.exclusive"
|
||||||
|
v-if="loopInstanceForm.asyncAfter || loopInstanceForm.asyncBefore"
|
||||||
|
label="排除"
|
||||||
|
@change="updateLoopAsync('exclusive')"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="重试周期"
|
||||||
|
prop="timeCycle"
|
||||||
|
v-if="loopInstanceForm.asyncAfter || loopInstanceForm.asyncBefore"
|
||||||
|
key="timeCycle"
|
||||||
|
>
|
||||||
|
<el-input v-model="loopInstanceForm.timeCycle" clearable @change="updateLoopTimeCycle" />
|
||||||
|
</el-form-item>
|
||||||
|
</template>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { onMounted, reactive, ref, watch, onBeforeUnmount,toRaw,nextTick } from 'vue'
|
||||||
|
defineOptions({ name: 'ElementMultiInstance' })
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
businessObject: Object,
|
||||||
|
type: String
|
||||||
|
})
|
||||||
|
const prefix = inject('prefix')
|
||||||
|
const loopCharacteristics = ref('')
|
||||||
|
//默认配置,用来覆盖原始不存在的选项,避免报错
|
||||||
|
const defaultLoopInstanceForm = ref({
|
||||||
|
completionCondition: '',
|
||||||
|
loopCardinality: '',
|
||||||
|
extensionElements: [],
|
||||||
|
asyncAfter: false,
|
||||||
|
asyncBefore: false,
|
||||||
|
exclusive: false
|
||||||
|
})
|
||||||
|
const loopInstanceForm = ref<any>({})
|
||||||
|
const bpmnElement = ref(null)
|
||||||
|
const multiLoopInstance = ref(null)
|
||||||
|
const bpmnInstances = () => (window as any)?.bpmnInstances
|
||||||
|
|
||||||
|
const getElementLoop = (businessObject) => {
|
||||||
|
if (!businessObject.loopCharacteristics) {
|
||||||
|
loopCharacteristics.value = 'Null'
|
||||||
|
loopInstanceForm.value = {}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (businessObject.loopCharacteristics.$type === 'bpmn:StandardLoopCharacteristics') {
|
||||||
|
loopCharacteristics.value = 'StandardLoop'
|
||||||
|
loopInstanceForm.value = {}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (businessObject.loopCharacteristics.isSequential) {
|
||||||
|
loopCharacteristics.value = 'SequentialMultiInstance'
|
||||||
|
} else {
|
||||||
|
loopCharacteristics.value = 'ParallelMultiInstance'
|
||||||
|
}
|
||||||
|
// 合并配置
|
||||||
|
loopInstanceForm.value = {
|
||||||
|
...defaultLoopInstanceForm.value,
|
||||||
|
...businessObject.loopCharacteristics,
|
||||||
|
completionCondition: businessObject.loopCharacteristics?.completionCondition?.body ?? '',
|
||||||
|
loopCardinality: businessObject.loopCharacteristics?.loopCardinality?.body ?? ''
|
||||||
|
}
|
||||||
|
// 保留当前元素 businessObject 上的 loopCharacteristics 实例
|
||||||
|
multiLoopInstance.value = bpmnInstances().bpmnElement.businessObject.loopCharacteristics
|
||||||
|
// 更新表单
|
||||||
|
if (
|
||||||
|
businessObject.loopCharacteristics.extensionElements &&
|
||||||
|
businessObject.loopCharacteristics.extensionElements.values &&
|
||||||
|
businessObject.loopCharacteristics.extensionElements.values.length
|
||||||
|
) {
|
||||||
|
loopInstanceForm.value['timeCycle'] =
|
||||||
|
businessObject.loopCharacteristics.extensionElements.values[0].body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const changeLoopCharacteristicsType = (type) => {
|
||||||
|
// this.loopInstanceForm = { ...this.defaultLoopInstanceForm }; // 切换类型取消原表单配置
|
||||||
|
// 取消多实例配置
|
||||||
|
if (type === 'Null') {
|
||||||
|
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
|
||||||
|
loopCharacteristics: null
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 配置循环
|
||||||
|
if (type === 'StandardLoop') {
|
||||||
|
const loopCharacteristicsObject = bpmnInstances().moddle.create(
|
||||||
|
'bpmn:StandardLoopCharacteristics'
|
||||||
|
)
|
||||||
|
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
|
||||||
|
loopCharacteristics: loopCharacteristicsObject
|
||||||
|
})
|
||||||
|
multiLoopInstance.value = null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 时序
|
||||||
|
if (type === 'SequentialMultiInstance') {
|
||||||
|
multiLoopInstance.value = bpmnInstances().moddle.create(
|
||||||
|
'bpmn:MultiInstanceLoopCharacteristics',
|
||||||
|
{ isSequential: true }
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
multiLoopInstance.value = bpmnInstances().moddle.create(
|
||||||
|
'bpmn:MultiInstanceLoopCharacteristics',
|
||||||
|
{ collection: '${coll_userList}' }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
|
||||||
|
loopCharacteristics: toRaw(multiLoopInstance.value)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 循环基数
|
||||||
|
const updateLoopCardinality = (cardinality) => {
|
||||||
|
let loopCardinality = null
|
||||||
|
if (cardinality && cardinality.length) {
|
||||||
|
loopCardinality = bpmnInstances().moddle.create('bpmn:FormalExpression', {
|
||||||
|
body: cardinality
|
||||||
|
})
|
||||||
|
}
|
||||||
|
bpmnInstances().modeling.updateModdleProperties(
|
||||||
|
toRaw(bpmnElement.value),
|
||||||
|
multiLoopInstance.value,
|
||||||
|
{
|
||||||
|
loopCardinality
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 完成条件
|
||||||
|
const updateLoopCondition = (condition) => {
|
||||||
|
let completionCondition = null
|
||||||
|
if (condition && condition.length) {
|
||||||
|
completionCondition = bpmnInstances().moddle.create('bpmn:FormalExpression', {
|
||||||
|
body: condition
|
||||||
|
})
|
||||||
|
}
|
||||||
|
bpmnInstances().modeling.updateModdleProperties(
|
||||||
|
toRaw(bpmnElement.value),
|
||||||
|
multiLoopInstance.value,
|
||||||
|
{
|
||||||
|
completionCondition
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重试周期
|
||||||
|
const updateLoopTimeCycle = (timeCycle) => {
|
||||||
|
const extensionElements = bpmnInstances().moddle.create('bpmn:ExtensionElements', {
|
||||||
|
values: [
|
||||||
|
bpmnInstances().moddle.create(`${prefix}:FailedJobRetryTimeCycle`, {
|
||||||
|
body: timeCycle
|
||||||
|
})
|
||||||
|
]
|
||||||
|
})
|
||||||
|
bpmnInstances().modeling.updateModdleProperties(
|
||||||
|
toRaw(bpmnElement.value),
|
||||||
|
multiLoopInstance.value,
|
||||||
|
{
|
||||||
|
extensionElements
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 直接更新的基础信息
|
||||||
|
const updateLoopBase = () => {
|
||||||
|
bpmnInstances().modeling.updateModdleProperties(
|
||||||
|
toRaw(bpmnElement.value),
|
||||||
|
multiLoopInstance.value,
|
||||||
|
{
|
||||||
|
collection: loopInstanceForm.value.collection || null,
|
||||||
|
elementVariable: loopInstanceForm.value.elementVariable || null
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 各异步状态
|
||||||
|
const updateLoopAsync = (key) => {
|
||||||
|
const { asyncBefore, asyncAfter } = loopInstanceForm.value
|
||||||
|
let asyncAttr = Object.create(null)
|
||||||
|
if (!asyncBefore && !asyncAfter) {
|
||||||
|
// this.$set(this.loopInstanceForm, "exclusive", false);
|
||||||
|
loopInstanceForm.value['exclusive'] = false
|
||||||
|
asyncAttr = { asyncBefore: false, asyncAfter: false, exclusive: false, extensionElements: null }
|
||||||
|
} else {
|
||||||
|
asyncAttr[key] = loopInstanceForm.value[key]
|
||||||
|
}
|
||||||
|
bpmnInstances().modeling.updateModdleProperties(
|
||||||
|
toRaw(bpmnElement.value),
|
||||||
|
multiLoopInstance.value,
|
||||||
|
asyncAttr
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const changeConfig = (config) => {
|
||||||
|
if (config === '依次审批') {
|
||||||
|
changeLoopCharacteristicsType('SequentialMultiInstance')
|
||||||
|
updateLoopCardinality('1')
|
||||||
|
updateLoopCondition('${ nrOfCompletedInstances >= nrOfInstances }')
|
||||||
|
} else if (config === '会签') {
|
||||||
|
changeLoopCharacteristicsType('ParallelMultiInstance')
|
||||||
|
updateLoopCondition('${ nrOfCompletedInstances >= nrOfInstances }')
|
||||||
|
} else if (config === '或签') {
|
||||||
|
changeLoopCharacteristicsType('ParallelMultiInstance')
|
||||||
|
updateLoopCondition('${ nrOfCompletedInstances > 0 }')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
multiLoopInstance.value = null
|
||||||
|
bpmnElement.value = null
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.businessObject,
|
||||||
|
(val) => {
|
||||||
|
bpmnElement.value = bpmnInstances().bpmnElement
|
||||||
|
getElementLoop(val)
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
<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"
|
||||||
|
resize="vertical"
|
||||||
|
:autosize="{ minRows: 2, maxRows: 4 }"
|
||||||
|
@input="updateDocumentation"
|
||||||
|
@blur="updateDocumentation"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
defineOptions({ name: 'ElementOtherConfig' })
|
||||||
|
import { onMounted, reactive, ref, watch, onBeforeUnmount,toRaw,nextTick } from 'vue'
|
||||||
|
const props = defineProps({
|
||||||
|
id: String
|
||||||
|
})
|
||||||
|
const documentation = ref('')
|
||||||
|
const bpmnElement = ref()
|
||||||
|
const bpmnInstances = () => (window as any).bpmnInstances
|
||||||
|
const updateDocumentation = () => {
|
||||||
|
;(bpmnElement.value && bpmnElement.value.id === props.id) ||
|
||||||
|
(bpmnElement.value = bpmnInstances().elementRegistry.get(props.id))
|
||||||
|
const documentations = bpmnInstances().bpmnFactory.create('bpmn:Documentation', {
|
||||||
|
text: documentation.value
|
||||||
|
})
|
||||||
|
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
|
||||||
|
documentation: [documentations]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
bpmnElement.value = null
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.id,
|
||||||
|
(id) => {
|
||||||
|
if (id && id.length) {
|
||||||
|
nextTick(() => {
|
||||||
|
const documentations = bpmnInstances().bpmnElement.businessObject?.documentation
|
||||||
|
documentation.value = documentations && documentations.length ? documentations[0].text : ''
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
documentation.value = ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,154 @@
|
|||||||
|
<template>
|
||||||
|
<div class="panel-tab__content">
|
||||||
|
<el-table :data="elementPropertyList" max-height="240" fit border>
|
||||||
|
<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="110px">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button link @click="openAttributesForm(scope.row, scope.$index)" size="small">编辑</el-button>
|
||||||
|
<el-divider direction="vertical" />
|
||||||
|
<el-button
|
||||||
|
link
|
||||||
|
size="small"
|
||||||
|
style="color: #ff4d4f"
|
||||||
|
@click="removeAttributes(scope.row, scope.$index)"
|
||||||
|
>
|
||||||
|
移除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div class="element-drawer__button">
|
||||||
|
<el-button type="primary" icon="el-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" ref="attributeFormRef">
|
||||||
|
<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 #footer>
|
||||||
|
<el-button @click="propertyFormModelVisible = false">取 消</el-button>
|
||||||
|
<el-button type="primary" @click="saveAttribute">确 定</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, watch, nextTick, inject, toRaw } from 'vue'
|
||||||
|
import { ElMessageBox } from 'element-plus'
|
||||||
|
defineOptions({ name: 'ElementProperties' })
|
||||||
|
const props = defineProps({
|
||||||
|
id: String,
|
||||||
|
type: String
|
||||||
|
})
|
||||||
|
const prefix = inject('prefix')
|
||||||
|
// const width = inject('width')
|
||||||
|
|
||||||
|
const elementPropertyList = ref<any[]>([])
|
||||||
|
const propertyForm = ref<any>({})
|
||||||
|
const editingPropertyIndex = ref(-1)
|
||||||
|
const propertyFormModelVisible = ref(false)
|
||||||
|
const bpmnElement = ref()
|
||||||
|
const otherExtensionList = ref()
|
||||||
|
const bpmnElementProperties = ref()
|
||||||
|
const bpmnElementPropertyList = ref()
|
||||||
|
const attributeFormRef = ref()
|
||||||
|
const bpmnInstances = () => (window as any)?.bpmnInstances
|
||||||
|
|
||||||
|
const resetAttributesList = () => {
|
||||||
|
console.log(window, 'windowwindowwindowwindowwindowwindowwindow')
|
||||||
|
bpmnElement.value = bpmnInstances().bpmnElement
|
||||||
|
otherExtensionList.value = [] // 其他扩展配置
|
||||||
|
bpmnElementProperties.value =
|
||||||
|
// bpmnElement.value.businessObject?.extensionElements?.filter((ex) => {
|
||||||
|
bpmnElement.value.businessObject?.extensionElements?.values.filter(ex => {
|
||||||
|
if (ex.$type !== `${prefix}:Properties`) {
|
||||||
|
otherExtensionList.value.push(ex)
|
||||||
|
}
|
||||||
|
return ex.$type === `${prefix}:Properties`
|
||||||
|
}) ?? []
|
||||||
|
|
||||||
|
// 保存所有的 扩展属性字段
|
||||||
|
bpmnElementPropertyList.value = bpmnElementProperties.value.reduce((pre, current) => pre.concat(current.values), [])
|
||||||
|
// 复制 显示
|
||||||
|
elementPropertyList.value = JSON.parse(JSON.stringify(bpmnElementPropertyList.value ?? []))
|
||||||
|
}
|
||||||
|
const openAttributesForm = (attr, index) => {
|
||||||
|
editingPropertyIndex.value = index
|
||||||
|
propertyForm.value = index === -1 ? {} : JSON.parse(JSON.stringify(attr))
|
||||||
|
propertyFormModelVisible.value = true
|
||||||
|
nextTick(() => {
|
||||||
|
if (attributeFormRef.value) attributeFormRef.value.clearValidate()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const removeAttributes = (attr, index) => {
|
||||||
|
console.log(attr, 'attr')
|
||||||
|
ElMessageBox.confirm('确认移除该属性吗?', '提示', {
|
||||||
|
confirmButtonText: '确 认',
|
||||||
|
cancelButtonText: '取 消'
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
elementPropertyList.value.splice(index, 1)
|
||||||
|
bpmnElementPropertyList.value.splice(index, 1)
|
||||||
|
// 新建一个属性字段的保存列表
|
||||||
|
const propertiesObject = bpmnInstances().moddle.create(`${prefix}:Properties`, {
|
||||||
|
values: bpmnElementPropertyList.value
|
||||||
|
})
|
||||||
|
updateElementExtensions(propertiesObject)
|
||||||
|
resetAttributesList()
|
||||||
|
})
|
||||||
|
.catch(() => console.info('操作取消'))
|
||||||
|
}
|
||||||
|
const saveAttribute = () => {
|
||||||
|
console.log(propertyForm.value, 'propertyForm.value')
|
||||||
|
const { name, value } = propertyForm.value
|
||||||
|
if (editingPropertyIndex.value !== -1) {
|
||||||
|
bpmnInstances().modeling.updateModdleProperties(
|
||||||
|
toRaw(bpmnElement.value),
|
||||||
|
toRaw(bpmnElementPropertyList.value)[toRaw(editingPropertyIndex.value)],
|
||||||
|
{
|
||||||
|
name,
|
||||||
|
value
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// 新建属性字段
|
||||||
|
const newPropertyObject = bpmnInstances().moddle.create(`${prefix}:Property`, {
|
||||||
|
name,
|
||||||
|
value
|
||||||
|
})
|
||||||
|
// 新建一个属性字段的保存列表
|
||||||
|
const propertiesObject = bpmnInstances().moddle.create(`${prefix}:Properties`, {
|
||||||
|
values: bpmnElementPropertyList.value.concat([newPropertyObject])
|
||||||
|
})
|
||||||
|
updateElementExtensions(propertiesObject)
|
||||||
|
}
|
||||||
|
propertyFormModelVisible.value = false
|
||||||
|
resetAttributesList()
|
||||||
|
}
|
||||||
|
const updateElementExtensions = properties => {
|
||||||
|
const extensions = bpmnInstances().moddle.create('bpmn:ExtensionElements', {
|
||||||
|
values: otherExtensionList.value.concat([properties])
|
||||||
|
})
|
||||||
|
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
|
||||||
|
extensionElements: extensions
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.id,
|
||||||
|
val => {
|
||||||
|
if (val) {
|
||||||
|
val && val.length && resetAttributesList()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
<template>
|
||||||
|
<div class="panel-tab__content">
|
||||||
|
<div class="panel-tab__content--title">
|
||||||
|
<span>
|
||||||
|
<Icon icon="ep:menu" style="margin-right: 8px; color: #555" />
|
||||||
|
消息列表
|
||||||
|
</span>
|
||||||
|
<el-button type="primary" icon="el-icon-Plus" @click="openModel('message')">创建新消息</el-button>
|
||||||
|
</div>
|
||||||
|
<el-table :data="messageList" 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 #eee">
|
||||||
|
<span>
|
||||||
|
<Icon icon="ep:menu" style="margin-right: 8px; color: #555" />
|
||||||
|
信号列表
|
||||||
|
</span>
|
||||||
|
<el-button type="primary" icon="el-icon-Plus" @click="openModel('signal')">创建新信号</el-button>
|
||||||
|
</div>
|
||||||
|
<el-table :data="signalList" 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="dialogVisible"
|
||||||
|
:title="modelConfig.title"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
width="400px"
|
||||||
|
append-to-body
|
||||||
|
destroy-on-close
|
||||||
|
>
|
||||||
|
<el-form :model="modelObjectForm" label-width="90px">
|
||||||
|
<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 #footer>
|
||||||
|
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||||
|
<el-button type="primary" @click="addNewObject">保 存</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { onMounted, reactive, ref, watch, onBeforeUnmount, toRaw, nextTick, computed } from 'vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
defineOptions({ name: 'SignalAndMassage' })
|
||||||
|
|
||||||
|
const signalList = ref<any[]>([])
|
||||||
|
const messageList = ref<any[]>([])
|
||||||
|
const dialogVisible = ref(false)
|
||||||
|
const modelType = ref('')
|
||||||
|
const modelObjectForm = ref<any>({})
|
||||||
|
const rootElements = ref()
|
||||||
|
const messageIdMap = ref()
|
||||||
|
const signalIdMap = ref()
|
||||||
|
const modelConfig = computed(() => {
|
||||||
|
if (modelType.value === 'message') {
|
||||||
|
return { title: '创建消息', idLabel: '消息ID', nameLabel: '消息名称' }
|
||||||
|
} else {
|
||||||
|
return { title: '创建信号', idLabel: '信号ID', nameLabel: '信号名称' }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const bpmnInstances = () => (window as any)?.bpmnInstances
|
||||||
|
|
||||||
|
const initDataList = () => {
|
||||||
|
console.log(window, 'window')
|
||||||
|
rootElements.value = bpmnInstances().modeler.getDefinitions().rootElements
|
||||||
|
messageIdMap.value = {}
|
||||||
|
signalIdMap.value = {}
|
||||||
|
messageList.value = []
|
||||||
|
signalList.value = []
|
||||||
|
rootElements.value.forEach(el => {
|
||||||
|
if (el.$type === 'bpmn:Message') {
|
||||||
|
messageIdMap.value[el.id] = true
|
||||||
|
messageList.value.push({ ...el })
|
||||||
|
}
|
||||||
|
if (el.$type === 'bpmn:Signal') {
|
||||||
|
signalIdMap.value[el.id] = true
|
||||||
|
signalList.value.push({ ...el })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const openModel = type => {
|
||||||
|
modelType.value = type
|
||||||
|
modelObjectForm.value = {}
|
||||||
|
dialogVisible.value = true
|
||||||
|
}
|
||||||
|
const addNewObject = () => {
|
||||||
|
if (modelType.value === 'message') {
|
||||||
|
if (messageIdMap.value[modelObjectForm.value.id]) {
|
||||||
|
ElMessage.error('该消息已存在,请修改id后重新保存')
|
||||||
|
}
|
||||||
|
const messageRef = bpmnInstances().moddle.create('bpmn:Message', modelObjectForm.value)
|
||||||
|
rootElements.value.push(messageRef)
|
||||||
|
} else {
|
||||||
|
if (signalIdMap.value[modelObjectForm.value.id]) {
|
||||||
|
ElMessage.error('该信号已存在,请修改id后重新保存')
|
||||||
|
}
|
||||||
|
const signalRef = bpmnInstances().moddle.create('bpmn:Signal', modelObjectForm.value)
|
||||||
|
rootElements.value.push(signalRef)
|
||||||
|
}
|
||||||
|
dialogVisible.value = false
|
||||||
|
initDataList()
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
initDataList()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
<template>
|
||||||
|
<div class="panel-tab__content">
|
||||||
|
<el-form size="small" label-width="90px">
|
||||||
|
<!-- add by 芋艿:由于「异步延续」暂时用不到,所以这里 display 为 none -->
|
||||||
|
<el-form-item label="异步延续" style="display: none">
|
||||||
|
<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 lang="ts" setup>
|
||||||
|
import UserTask from './task-components/UserTask.vue'
|
||||||
|
import ScriptTask from './task-components/ScriptTask.vue'
|
||||||
|
import ReceiveTask from './task-components/ReceiveTask.vue'
|
||||||
|
|
||||||
|
import { onMounted, reactive, ref, watch, onBeforeUnmount,toRaw,nextTick } from 'vue'
|
||||||
|
defineOptions({ name: 'ElementTaskConfig' })
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
id: String,
|
||||||
|
type: String
|
||||||
|
})
|
||||||
|
const taskConfigForm = ref({
|
||||||
|
asyncAfter: false,
|
||||||
|
asyncBefore: false,
|
||||||
|
exclusive: false
|
||||||
|
})
|
||||||
|
const witchTaskComponent = ref()
|
||||||
|
const installedComponent = ref({
|
||||||
|
// 手工任务与普通任务一致,不需要其他配置
|
||||||
|
// 接收消息任务,需要在全局下插入新的消息实例,并在该节点下的 messageRef 属性绑定该实例
|
||||||
|
// 发送任务、服务任务、业务规则任务共用一个相同配置
|
||||||
|
UserTask: 'UserTask', // 用户任务配置
|
||||||
|
ScriptTask: 'ScriptTask', // 脚本任务配置
|
||||||
|
ReceiveTask: 'ReceiveTask' // 消息接收任务
|
||||||
|
})
|
||||||
|
const bpmnElement = ref()
|
||||||
|
|
||||||
|
const bpmnInstances = () => (window as any).bpmnInstances
|
||||||
|
const changeTaskAsync = () => {
|
||||||
|
if (!taskConfigForm.value.asyncBefore && !taskConfigForm.value.asyncAfter) {
|
||||||
|
taskConfigForm.value.exclusive = false
|
||||||
|
}
|
||||||
|
bpmnInstances().modeling.updateProperties(bpmnInstances().bpmnElement, {
|
||||||
|
...taskConfigForm.value
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.id,
|
||||||
|
() => {
|
||||||
|
bpmnElement.value = bpmnInstances().bpmnElement
|
||||||
|
taskConfigForm.value.asyncBefore = bpmnElement.value?.businessObject?.asyncBefore
|
||||||
|
taskConfigForm.value.asyncAfter = bpmnElement.value?.businessObject?.asyncAfter
|
||||||
|
taskConfigForm.value.exclusive = bpmnElement.value?.businessObject?.exclusive
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
watch(
|
||||||
|
() => props.type,
|
||||||
|
() => {
|
||||||
|
// witchTaskComponent.value = installedComponent.value[props.type]
|
||||||
|
if (props.type == installedComponent.value.UserTask) {
|
||||||
|
witchTaskComponent.value = UserTask
|
||||||
|
}
|
||||||
|
if (props.type == installedComponent.value.ScriptTask) {
|
||||||
|
witchTaskComponent.value = ScriptTask
|
||||||
|
}
|
||||||
|
if (props.type == installedComponent.value.ReceiveTask) {
|
||||||
|
witchTaskComponent.value = ReceiveTask
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
<!-- 表达式选择 -->
|
||||||
|
<template>
|
||||||
|
<Dialog title="请选择表达式" v-model="dialogVisible" width="1024px">
|
||||||
|
<ContentWrap>
|
||||||
|
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
|
||||||
|
<el-table-column label="名字" align="center" prop="name" />
|
||||||
|
<el-table-column label="表达式" align="center" prop="expression" />
|
||||||
|
<el-table-column label="操作" align="center">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button link type="primary" @click="select(scope.row)">选择</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<!-- 分页 -->
|
||||||
|
<Pagination
|
||||||
|
:total="total"
|
||||||
|
v-model:page="queryParams.pageNo"
|
||||||
|
v-model:limit="queryParams.pageSize"
|
||||||
|
@pagination="getList"
|
||||||
|
/>
|
||||||
|
</ContentWrap>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { onMounted, reactive, ref, watch, onBeforeUnmount } from 'vue'
|
||||||
|
import { ProcessExpressionApi, ProcessExpressionVO } from '@/api/bpm/processExpression'
|
||||||
|
|
||||||
|
/** BPM 流程 表单 */
|
||||||
|
defineOptions({ name: 'ProcessExpressionDialog' })
|
||||||
|
|
||||||
|
|
||||||
|
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||||
|
const loading = ref(true) // 列表的加载中
|
||||||
|
const list = ref<ProcessExpressionVO[]>([]) // 列表的数据
|
||||||
|
const total = ref(0) // 列表的总页数
|
||||||
|
const queryParams = reactive({
|
||||||
|
pageNo: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
type: undefined
|
||||||
|
})
|
||||||
|
|
||||||
|
/** 打开弹窗 */
|
||||||
|
const open = async (type: string) => {
|
||||||
|
dialogVisible.value = true
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
queryParams.pageNo = 1
|
||||||
|
queryParams.type = type
|
||||||
|
const data = await ProcessExpressionApi.getProcessExpressionPage(queryParams)
|
||||||
|
list.value = data.list
|
||||||
|
total.value = data.total
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||||
|
|
||||||
|
/** 提交表单 */
|
||||||
|
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||||
|
const select = async row => {
|
||||||
|
dialogVisible.value = false
|
||||||
|
// 发送操作成功的事件
|
||||||
|
emit('select', row)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
<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="key in Object.keys(messageMap)"
|
||||||
|
:value="key"
|
||||||
|
:label="messageMap[key]"
|
||||||
|
:key="key"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
<el-button type="primary" icon="el-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">
|
||||||
|
<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 #footer>
|
||||||
|
<el-button size="small" type="primary" @click="createNewMessage">确 认</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
defineOptions({ name: 'ReceiveTask' })
|
||||||
|
const props = defineProps({
|
||||||
|
id: String,
|
||||||
|
type: String
|
||||||
|
})
|
||||||
|
|
||||||
|
const message = useMessage()
|
||||||
|
|
||||||
|
const bindMessageId = ref('')
|
||||||
|
const newMessageForm = ref<any>({})
|
||||||
|
const messageMap = ref<any>({})
|
||||||
|
const messageModelVisible = ref(false)
|
||||||
|
const bpmnElement = ref<any>()
|
||||||
|
const bpmnMessageRefsMap = ref<any>()
|
||||||
|
const bpmnRootElements = ref<any>()
|
||||||
|
|
||||||
|
const bpmnInstances = () => (window as any).bpmnInstances
|
||||||
|
const getBindMessage = () => {
|
||||||
|
bpmnElement.value = bpmnInstances().bpmnElement
|
||||||
|
bindMessageId.value = bpmnElement.value.businessObject?.messageRef?.id || '-1'
|
||||||
|
}
|
||||||
|
const openMessageModel = () => {
|
||||||
|
messageModelVisible.value = true
|
||||||
|
newMessageForm.value = {}
|
||||||
|
}
|
||||||
|
const createNewMessage = () => {
|
||||||
|
if (messageMap.value[newMessageForm.value.id]) {
|
||||||
|
message.error('该消息已存在,请修改id后重新保存')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const newMessage = bpmnInstances().moddle.create('bpmn:Message', newMessageForm.value)
|
||||||
|
bpmnRootElements.value.push(newMessage)
|
||||||
|
messageMap.value[newMessageForm.value.id] = newMessageForm.value.name
|
||||||
|
bpmnMessageRefsMap.value[newMessageForm.value.id] = newMessage
|
||||||
|
messageModelVisible.value = false
|
||||||
|
}
|
||||||
|
const updateTaskMessage = messageId => {
|
||||||
|
if (messageId === '-1') {
|
||||||
|
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
|
||||||
|
messageRef: null
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
|
||||||
|
messageRef: bpmnMessageRefsMap.value[messageId]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
bpmnMessageRefsMap.value = Object.create(null)
|
||||||
|
bpmnRootElements.value = bpmnInstances().modeler.getDefinitions().rootElements
|
||||||
|
bpmnRootElements.value
|
||||||
|
.filter(el => el.$type === 'bpmn:Message')
|
||||||
|
.forEach(m => {
|
||||||
|
bpmnMessageRefsMap.value[m.id] = m
|
||||||
|
messageMap.value[m.id] = m.name
|
||||||
|
})
|
||||||
|
messageMap.value['-1'] = '无'
|
||||||
|
})
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
bpmnElement.value = null
|
||||||
|
})
|
||||||
|
watch(
|
||||||
|
() => props.id,
|
||||||
|
() => {
|
||||||
|
// bpmnElement.value = bpmnInstances().bpmnElement
|
||||||
|
nextTick(() => {
|
||||||
|
getBindMessage()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
<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 lang="ts" setup>
|
||||||
|
defineOptions({ name: 'ScriptTask' })
|
||||||
|
const props = defineProps({
|
||||||
|
id: String,
|
||||||
|
type: String
|
||||||
|
})
|
||||||
|
const defaultTaskForm = ref({
|
||||||
|
scriptFormat: '',
|
||||||
|
script: '',
|
||||||
|
resource: '',
|
||||||
|
resultVariable: ''
|
||||||
|
})
|
||||||
|
const scriptTaskForm = ref<any>({})
|
||||||
|
const bpmnElement = ref()
|
||||||
|
|
||||||
|
const bpmnInstances = () => (window as any)?.bpmnInstances
|
||||||
|
|
||||||
|
const resetTaskForm = () => {
|
||||||
|
for (let key in defaultTaskForm.value) {
|
||||||
|
let value = bpmnElement.value?.businessObject[key] || defaultTaskForm.value[key]
|
||||||
|
scriptTaskForm.value[key] = value
|
||||||
|
}
|
||||||
|
scriptTaskForm.value.scriptType = scriptTaskForm.value.script ? 'inline' : 'external'
|
||||||
|
}
|
||||||
|
const updateElementTask = () => {
|
||||||
|
let taskAttr = Object.create(null)
|
||||||
|
taskAttr.scriptFormat = scriptTaskForm.value.scriptFormat || null
|
||||||
|
taskAttr.resultVariable = scriptTaskForm.value.resultVariable || null
|
||||||
|
if (scriptTaskForm.value.scriptType === 'inline') {
|
||||||
|
taskAttr.script = scriptTaskForm.value.script || null
|
||||||
|
taskAttr.resource = null
|
||||||
|
} else {
|
||||||
|
taskAttr.resource = scriptTaskForm.value.resource || null
|
||||||
|
taskAttr.script = null
|
||||||
|
}
|
||||||
|
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), taskAttr)
|
||||||
|
}
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
bpmnElement.value = null
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.id,
|
||||||
|
() => {
|
||||||
|
bpmnElement.value = bpmnInstances().bpmnElement
|
||||||
|
nextTick(() => {
|
||||||
|
resetTaskForm()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,234 @@
|
|||||||
|
<template>
|
||||||
|
<el-form label-width="100px">
|
||||||
|
<el-form-item label="规则类型" prop="candidateStrategy">
|
||||||
|
<el-select
|
||||||
|
v-model="userTaskForm.candidateStrategy"
|
||||||
|
clearable
|
||||||
|
style="width: 100%"
|
||||||
|
@change="changeCandidateStrategy"
|
||||||
|
>
|
||||||
|
<!-- <el-option
|
||||||
|
v-for="dict in getIntDictOptions(DICT_TYPE.BPM_TASK_CANDIDATE_STRATEGY)"
|
||||||
|
:key="dict.value"
|
||||||
|
:label="dict.label"
|
||||||
|
:value="dict.value"
|
||||||
|
/> -->
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
v-if="userTaskForm.candidateStrategy == 10"
|
||||||
|
label="指定角色"
|
||||||
|
prop="candidateParam"
|
||||||
|
>
|
||||||
|
<el-select
|
||||||
|
v-model="userTaskForm.candidateParam"
|
||||||
|
clearable
|
||||||
|
multiple
|
||||||
|
style="width: 100%"
|
||||||
|
@change="updateElementTask"
|
||||||
|
>
|
||||||
|
<el-option v-for="item in roleOptions" :key="item.id" :label="item.name" :value="item.id" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
v-if="userTaskForm.candidateStrategy == 20 || userTaskForm.candidateStrategy == 21"
|
||||||
|
label="指定部门"
|
||||||
|
prop="candidateParam"
|
||||||
|
span="24"
|
||||||
|
>
|
||||||
|
<el-tree-select
|
||||||
|
ref="treeRef"
|
||||||
|
v-model="userTaskForm.candidateParam"
|
||||||
|
:data="deptTreeOptions"
|
||||||
|
:props="defaultProps"
|
||||||
|
empty-text="加载中,请稍后"
|
||||||
|
multiple
|
||||||
|
node-key="id"
|
||||||
|
show-checkbox
|
||||||
|
@change="updateElementTask"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
v-if="userTaskForm.candidateStrategy == 22"
|
||||||
|
label="指定岗位"
|
||||||
|
prop="candidateParam"
|
||||||
|
span="24"
|
||||||
|
>
|
||||||
|
<el-select
|
||||||
|
v-model="userTaskForm.candidateParam"
|
||||||
|
clearable
|
||||||
|
multiple
|
||||||
|
style="width: 100%"
|
||||||
|
@change="updateElementTask"
|
||||||
|
>
|
||||||
|
<el-option v-for="item in postOptions" :key="item.id" :label="item.name" :value="item.id" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
v-if="userTaskForm.candidateStrategy == 30"
|
||||||
|
label="指定用户"
|
||||||
|
prop="candidateParam"
|
||||||
|
span="24"
|
||||||
|
>
|
||||||
|
<el-select
|
||||||
|
v-model="userTaskForm.candidateParam"
|
||||||
|
clearable
|
||||||
|
multiple
|
||||||
|
style="width: 100%"
|
||||||
|
@change="updateElementTask"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in userOptions"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.nickname"
|
||||||
|
:value="item.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
v-if="userTaskForm.candidateStrategy === 40"
|
||||||
|
label="指定用户组"
|
||||||
|
prop="candidateParam"
|
||||||
|
>
|
||||||
|
<el-select
|
||||||
|
v-model="userTaskForm.candidateParam"
|
||||||
|
clearable
|
||||||
|
multiple
|
||||||
|
style="width: 100%"
|
||||||
|
@change="updateElementTask"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in userGroupOptions"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.name"
|
||||||
|
:value="item.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
v-if="userTaskForm.candidateStrategy === 60"
|
||||||
|
label="流程表达式"
|
||||||
|
prop="candidateParam"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
type="textarea"
|
||||||
|
v-model="userTaskForm.candidateParam[0]"
|
||||||
|
clearable
|
||||||
|
style="width: 72%"
|
||||||
|
@change="updateElementTask"
|
||||||
|
/>
|
||||||
|
<el-button class="ml-5px" size="small" type="success" @click="openProcessExpressionDialog"
|
||||||
|
>选择表达式</el-button
|
||||||
|
>
|
||||||
|
<!-- 选择弹窗 -->
|
||||||
|
<ProcessExpressionDialog ref="processExpressionDialogRef" @select="selectProcessExpression" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
// import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||||
|
|
||||||
|
import { onMounted, reactive, ref, watch, onBeforeUnmount,toRaw,nextTick } from 'vue'
|
||||||
|
import { defaultProps, handleTree } from '@/utils/tree'
|
||||||
|
import * as RoleApi from '@/api/system/role'
|
||||||
|
import * as DeptApi from '@/api/system/dept'
|
||||||
|
import * as PostApi from '@/api/system/post'
|
||||||
|
import * as UserApi from '@/api/system/user'
|
||||||
|
import * as UserGroupApi from '@/api/bpm/userGroup'
|
||||||
|
import ProcessExpressionDialog from './ProcessExpressionDialog.vue'
|
||||||
|
|
||||||
|
defineOptions({ name: 'UserTask' })
|
||||||
|
const props = defineProps({
|
||||||
|
id: String,
|
||||||
|
type: String
|
||||||
|
})
|
||||||
|
const userTaskForm = ref({
|
||||||
|
candidateStrategy: undefined, // 分配规则
|
||||||
|
candidateParam: [] // 分配选项
|
||||||
|
})
|
||||||
|
const bpmnElement = ref()
|
||||||
|
const bpmnInstances = () => (window as any)?.bpmnInstances
|
||||||
|
|
||||||
|
const roleOptions = ref<RoleApi.RoleVO[]>([]) // 角色列表
|
||||||
|
const deptTreeOptions = ref() // 部门树
|
||||||
|
const postOptions = ref<PostApi.PostVO[]>([]) // 岗位列表
|
||||||
|
const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
|
||||||
|
const userGroupOptions = ref<UserGroupApi.UserGroupVO[]>([]) // 用户组列表
|
||||||
|
|
||||||
|
const resetTaskForm = () => {
|
||||||
|
const businessObject = bpmnElement.value.businessObject
|
||||||
|
if (!businessObject) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (businessObject.candidateStrategy != undefined) {
|
||||||
|
userTaskForm.value.candidateStrategy = parseInt(businessObject.candidateStrategy) as any
|
||||||
|
} else {
|
||||||
|
userTaskForm.value.candidateStrategy = undefined
|
||||||
|
}
|
||||||
|
if (businessObject.candidateParam && businessObject.candidateParam.length > 0) {
|
||||||
|
if (userTaskForm.value.candidateStrategy === 60) {
|
||||||
|
// 特殊:流程表达式,只有一个 input 输入框
|
||||||
|
userTaskForm.value.candidateParam = [businessObject.candidateParam]
|
||||||
|
} else {
|
||||||
|
userTaskForm.value.candidateParam = businessObject.candidateParam
|
||||||
|
.split(',')
|
||||||
|
.map((item) => +item)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
userTaskForm.value.candidateParam = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 更新 candidateStrategy 字段时,需要清空 candidateParam,并触发 bpmn 图更新 */
|
||||||
|
const changeCandidateStrategy = () => {
|
||||||
|
userTaskForm.value.candidateParam = []
|
||||||
|
updateElementTask()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 选中某个 options 时候,更新 bpmn 图 */
|
||||||
|
const updateElementTask = () => {
|
||||||
|
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
|
||||||
|
candidateStrategy: userTaskForm.value.candidateStrategy,
|
||||||
|
candidateParam: userTaskForm.value.candidateParam.join(',')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开监听器弹窗
|
||||||
|
const processExpressionDialogRef = ref()
|
||||||
|
const openProcessExpressionDialog = async () => {
|
||||||
|
processExpressionDialogRef.value.open()
|
||||||
|
}
|
||||||
|
const selectProcessExpression = (expression) => {
|
||||||
|
userTaskForm.value.candidateParam = [expression.expression]
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.id,
|
||||||
|
() => {
|
||||||
|
bpmnElement.value = bpmnInstances().bpmnElement
|
||||||
|
nextTick(() => {
|
||||||
|
resetTaskForm()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
// 获得角色列表
|
||||||
|
roleOptions.value = await RoleApi.getSimpleRoleList()
|
||||||
|
// 获得部门列表
|
||||||
|
const deptOptions = await DeptApi.getSimpleDeptList()
|
||||||
|
deptTreeOptions.value = handleTree(deptOptions, 'id')
|
||||||
|
// 获得岗位列表
|
||||||
|
postOptions.value = await PostApi.getSimplePostList()
|
||||||
|
// 获得用户列表
|
||||||
|
userOptions.value = await UserApi.getSimpleUserList()
|
||||||
|
// 获得用户组列表
|
||||||
|
userGroupOptions.value = await UserGroupApi.getUserGroupSimpleList()
|
||||||
|
})
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
bpmnElement.value = null
|
||||||
|
})
|
||||||
|
</script>
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
@import './process-designer.scss';
|
||||||
|
@import './process-panel.scss';
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
@import 'bpmn-js-token-simulation/assets/css/bpmn-js-token-simulation.css';
|
||||||
|
@import 'bpmn-js-token-simulation/assets/css/font-awesome.min.css';
|
||||||
|
@import 'bpmn-js-token-simulation/assets/css/normalize.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%, 60%);
|
||||||
|
transform: rotate(180deg) translate(271%, -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(0deg);
|
||||||
|
&:after {
|
||||||
|
// transform: rotate(90deg) translate(0, 60%);
|
||||||
|
transform: rotate(0deg) translate(-0%, -5%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.align.align-middle i {
|
||||||
|
transform: rotate(-90deg);
|
||||||
|
&:after {
|
||||||
|
// transform: rotate(90deg) translate(0, 60%);
|
||||||
|
transform: rotate(0deg) 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 .two-column .open {
|
||||||
|
.open {
|
||||||
|
// .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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,107 @@
|
|||||||
|
.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-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;
|
||||||
|
}
|
||||||
|
}
|
||||||
78
src/components/bpmnProcessDesigner/package/utils.ts
Normal file
78
src/components/bpmnProcessDesigner/package/utils.ts
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import { toRaw } from 'vue'
|
||||||
|
const bpmnInstances = () => (window as any)?.bpmnInstances
|
||||||
|
// 创建监听器实例
|
||||||
|
export function createListenerObject(options, isTask, prefix) {
|
||||||
|
debugger
|
||||||
|
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 = bpmnInstances().moddle.create('bpmn:FormalExpression', {
|
||||||
|
body: options.eventTimeDefinitions
|
||||||
|
})
|
||||||
|
const TimerEventDefinition = bpmnInstances().moddle.create('bpmn:TimerEventDefinition', {
|
||||||
|
id: `TimerEventDefinition_${uuid(8)}`,
|
||||||
|
[`time${options.eventDefinitionType.replace(/^\S/, (s) => s.toUpperCase())}`]: timeDefinition
|
||||||
|
})
|
||||||
|
listenerObj.eventDefinitions = [TimerEventDefinition]
|
||||||
|
}
|
||||||
|
return 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 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 bpmnInstances().moddle.create(`${prefix}:Script`, scriptConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新元素扩展属性
|
||||||
|
export function updateElementExtensions(element, extensionList) {
|
||||||
|
const extensions = bpmnInstances().moddle.create('bpmn:ExtensionElements', {
|
||||||
|
values: extensionList
|
||||||
|
})
|
||||||
|
bpmnInstances().modeling.updateProperties(toRaw(element), {
|
||||||
|
extensionElements: extensions
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建一个id
|
||||||
|
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
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
const hljs = require('highlight.js/lib/core')
|
||||||
|
hljs.registerLanguage('xml', require('highlight.js/lib/languages/xml'))
|
||||||
|
hljs.registerLanguage('json', require('highlight.js/lib/languages/json'))
|
||||||
|
|
||||||
|
module.exports = hljs
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import BpmnRenderer from 'bpmn-js/lib/draw/BpmnRenderer'
|
||||||
|
|
||||||
|
export default function CustomRenderer(config, eventBus, styles, pathMap, canvas, textRenderer) {
|
||||||
|
BpmnRenderer.call(this, config, eventBus, styles, pathMap, canvas, textRenderer, 2000)
|
||||||
|
|
||||||
|
this.handlers['label'] = function () {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const F = function () {} // 核心,利用空对象作为中介;
|
||||||
|
F.prototype = BpmnRenderer.prototype // 核心,将父类的原型赋值给空对象F;
|
||||||
|
CustomRenderer.prototype = new F() // 核心,将 F的实例赋值给子类;
|
||||||
|
CustomRenderer.prototype.constructor = CustomRenderer // 修复子类CustomRenderer的构造器指向,防止原型链的混乱;
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import CustomRenderer from './CustomRenderer'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
__init__: ['customRenderer'],
|
||||||
|
customRenderer: ['type', CustomRenderer]
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import CustomRules from './CustomRules'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
__init__: ['customRules'],
|
||||||
|
customRules: ['type', CustomRules]
|
||||||
|
}
|
||||||
25
src/components/bpmnProcessDesigner/src/translations.ts
Normal file
25
src/components/bpmnProcessDesigner/src/translations.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* 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 {
|
||||||
|
'Exclusive Gateway': 'Exklusives Gateway',
|
||||||
|
'Parallel Gateway': 'Paralleles Gateway',
|
||||||
|
'Inclusive Gateway': 'Inklusives Gateway',
|
||||||
|
'Complex Gateway': 'Komplexes Gateway',
|
||||||
|
'Event based Gateway': 'Ereignis-basiertes Gateway',
|
||||||
|
'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)': '升级启动事件 (非中断)'
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
//outside.js
|
||||||
|
|
||||||
|
const ctx = '@@clickoutsideContext'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
bind(el, binding, vnode) {
|
||||||
|
const ele = el
|
||||||
|
const documentHandler = (e) => {
|
||||||
|
if (!vnode.context || ele.contains(e.target)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// 调用指令回调
|
||||||
|
if (binding.expression) {
|
||||||
|
vnode.context[el[ctx].methodName](e)
|
||||||
|
} else {
|
||||||
|
el[ctx].bindingFn(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 将方法添加到ele
|
||||||
|
ele[ctx] = {
|
||||||
|
documentHandler,
|
||||||
|
methodName: binding.expression,
|
||||||
|
bindingFn: binding.value
|
||||||
|
}
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
document.addEventListener('touchstart', documentHandler) // 为document绑定事件
|
||||||
|
})
|
||||||
|
},
|
||||||
|
update(el, binding) {
|
||||||
|
const ele = el
|
||||||
|
ele[ctx].methodName = binding.expression
|
||||||
|
ele[ctx].bindingFn = binding.value
|
||||||
|
},
|
||||||
|
unbind(el) {
|
||||||
|
document.removeEventListener('touchstart', el[ctx].documentHandler) // 解绑
|
||||||
|
delete el[ctx]
|
||||||
|
}
|
||||||
|
}
|
||||||
10
src/components/bpmnProcessDesigner/src/utils/index.js
Normal file
10
src/components/bpmnProcessDesigner/src/utils/index.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
export function debounce(fn, delay = 500) {
|
||||||
|
let timer
|
||||||
|
return function (...args) {
|
||||||
|
if (timer) {
|
||||||
|
clearTimeout(timer)
|
||||||
|
timer = null
|
||||||
|
}
|
||||||
|
timer = setTimeout(fn.bind(this, ...args), delay)
|
||||||
|
}
|
||||||
|
}
|
||||||
50
src/components/bpmnProcessDesigner/src/utils/xml2json.js
Normal file
50
src/components/bpmnProcessDesigner/src/utils/xml2json.js
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
function xmlStr2XmlObj(xmlStr) {
|
||||||
|
let xmlObj = {}
|
||||||
|
if (document.all) {
|
||||||
|
const xmlDom = new window.ActiveXObject('Microsoft.XMLDOM')
|
||||||
|
xmlDom.loadXML(xmlStr)
|
||||||
|
xmlObj = xmlDom
|
||||||
|
} else {
|
||||||
|
xmlObj = new DOMParser().parseFromString(xmlStr, 'text/xml')
|
||||||
|
}
|
||||||
|
return xmlObj
|
||||||
|
}
|
||||||
|
|
||||||
|
function xml2json(xml) {
|
||||||
|
try {
|
||||||
|
let obj = {}
|
||||||
|
if (xml.children.length > 0) {
|
||||||
|
for (let i = 0; i < xml.children.length; i++) {
|
||||||
|
const item = xml.children.item(i)
|
||||||
|
const nodeName = item.nodeName
|
||||||
|
if (typeof obj[nodeName] == 'undefined') {
|
||||||
|
obj[nodeName] = xml2json(item)
|
||||||
|
} else {
|
||||||
|
if (typeof obj[nodeName].push == 'undefined') {
|
||||||
|
const old = obj[nodeName]
|
||||||
|
obj[nodeName] = []
|
||||||
|
obj[nodeName].push(old)
|
||||||
|
}
|
||||||
|
obj[nodeName].push(xml2json(item))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
obj = xml.textContent
|
||||||
|
}
|
||||||
|
return obj
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function xmlObj2json(xml) {
|
||||||
|
const xmlObj = xmlStr2XmlObj(xml)
|
||||||
|
console.log(xmlObj)
|
||||||
|
let jsonObj = {}
|
||||||
|
if (xmlObj.childNodes.length > 0) {
|
||||||
|
jsonObj = xml2json(xmlObj)
|
||||||
|
}
|
||||||
|
return jsonObj
|
||||||
|
}
|
||||||
|
|
||||||
|
export default xmlObj2json
|
||||||
@@ -236,7 +236,7 @@ const dotList: any = ref({
|
|||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const dom = document.getElementById('navigation-splitpanes')
|
const dom = document.getElementById('navigation-splitpanes')
|
||||||
if (dom) {
|
if (dom) {
|
||||||
size.value = Math.round((130 / dom.offsetHeight) * 100)
|
size.value = Math.round((180 / dom.offsetHeight) * 100)
|
||||||
}
|
}
|
||||||
datePickerRef.value.setTimeOptions([{ label: '周', value: 4 }])
|
datePickerRef.value.setTimeOptions([{ label: '周', value: 4 }])
|
||||||
datePickerRef.value.setInterval(4)
|
datePickerRef.value.setInterval(4)
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ const activeName = ref('6')
|
|||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const dom = document.getElementById('navigation-splitpanes')
|
const dom = document.getElementById('navigation-splitpanes')
|
||||||
if (dom) {
|
if (dom) {
|
||||||
size.value = Math.round((280 / dom.offsetHeight) * 100)
|
size.value = Math.round((180 / dom.offsetHeight) * 100)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const handleNodeClick = (data: any, node: any) => {
|
const handleNodeClick = (data: any, node: any) => {
|
||||||
|
|||||||
126
src/views/pqs/process/definition/editor/index.vue
Normal file
126
src/views/pqs/process/definition/editor/index.vue
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<!-- 流程设计器,负责绘制流程等 -->
|
||||||
|
<MyProcessDesigner
|
||||||
|
key="designer"
|
||||||
|
v-if="xmlString !== undefined"
|
||||||
|
v-model="xmlString"
|
||||||
|
:value="xmlString"
|
||||||
|
v-bind="controlForm"
|
||||||
|
keyboard
|
||||||
|
ref="processDesigner"
|
||||||
|
@init-finished="initModeler"
|
||||||
|
:additionalModel="controlForm.additionalModel"
|
||||||
|
@quit="emit('quit')"
|
||||||
|
@save="save"
|
||||||
|
/>
|
||||||
|
<!-- 流程属性器,负责编辑每个流程节点的属性 -->
|
||||||
|
<MyProcessPenal
|
||||||
|
key="penal"
|
||||||
|
:bpmnModeler="modeler as any"
|
||||||
|
:prefix="controlForm.prefix"
|
||||||
|
class="process-panel"
|
||||||
|
:model="model"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import * as ModelApi from '@/api/process-boot/bpm'
|
||||||
|
import { onMounted, provide, ref } from 'vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { MyProcessDesigner, MyProcessPenal } from '@/components/bpmnProcessDesigner/package'
|
||||||
|
// 自定义元素选中时的弹出菜单(修改 默认任务 为 用户任务)
|
||||||
|
import CustomContentPadProvider from '@/components/bpmnProcessDesigner/package/designer/plugins/content-pad'
|
||||||
|
// 自定义左侧菜单(修改 默认任务 为 用户任务)
|
||||||
|
import CustomPaletteProvider from '@/components/bpmnProcessDesigner/package/designer/plugins/palette'
|
||||||
|
|
||||||
|
defineOptions({ name: 'BpmModelEditor' })
|
||||||
|
|
||||||
|
const prop = defineProps({
|
||||||
|
model: { type: Object, default: undefined }
|
||||||
|
})
|
||||||
|
const xmlString = ref(undefined) // BPMN XML
|
||||||
|
const modeler = ref(null) // BPMN Modeler
|
||||||
|
const emit = defineEmits(['quit'])
|
||||||
|
const controlForm = ref({
|
||||||
|
simulation: true,
|
||||||
|
labelEditing: false,
|
||||||
|
labelVisible: false,
|
||||||
|
prefix: 'flowable',
|
||||||
|
headerButtonSize: 'mini',
|
||||||
|
additionalModel: [CustomContentPadProvider, CustomPaletteProvider]
|
||||||
|
})
|
||||||
|
const model = ref<ModelApi.ModelVO>() // 流程模型的信息
|
||||||
|
|
||||||
|
/** 初始化 modeler */
|
||||||
|
const initModeler = item => {
|
||||||
|
setTimeout(() => {
|
||||||
|
modeler.value = item
|
||||||
|
}, 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 添加/修改模型 */
|
||||||
|
const save = async bpmnXml => {
|
||||||
|
const data = {
|
||||||
|
// name
|
||||||
|
// flowableXml
|
||||||
|
// category
|
||||||
|
...model.value,
|
||||||
|
flowableXml: bpmnXml, // bpmnXml 只是初始化流程图,后续修改无法通过它获得
|
||||||
|
category: '',
|
||||||
|
name: ''
|
||||||
|
} as unknown as ModelApi.ModelVO
|
||||||
|
// 提交
|
||||||
|
if (data.id) {
|
||||||
|
await ModelApi.updateModel(data)
|
||||||
|
ElMessage.success('修改成功')
|
||||||
|
} else {
|
||||||
|
await ModelApi.createModel(data)
|
||||||
|
ElMessage.success('新增成功')
|
||||||
|
}
|
||||||
|
// 跳转回去
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 关闭按钮 */
|
||||||
|
const close = () => {}
|
||||||
|
const randomStr = () => {
|
||||||
|
return Math.random().toString(36).slice(-8)
|
||||||
|
}
|
||||||
|
/** 初始化 */
|
||||||
|
onMounted(async () => {
|
||||||
|
const data: any = {}
|
||||||
|
if (prop.model?.deploymentId === undefined) {
|
||||||
|
if (!data.bpmnXml) {
|
||||||
|
// 首次创建的 Model 模型,它是没有 bpmnXml,此时需要给它一个默认的
|
||||||
|
data.bpmnXml = ` <?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.activiti.org/processdef">
|
||||||
|
<process id="${data.key}" name="${data.name}" />
|
||||||
|
<bpmndi:BPMNDiagram id="BPMNDiagram">
|
||||||
|
<bpmndi:BPMNPlane id="${data.key}_di" bpmnElement="${data.key}" />
|
||||||
|
</bpmndi:BPMNDiagram>
|
||||||
|
</definitions>`
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await ModelApi.readXml(prop.model?.deploymentId).then((res: any) => {
|
||||||
|
data.bpmnXml = res.data
|
||||||
|
})
|
||||||
|
// const data: any = {}
|
||||||
|
}
|
||||||
|
// 查询模型
|
||||||
|
|
||||||
|
model.value = {
|
||||||
|
...data,
|
||||||
|
bpmnXml: undefined // 清空 bpmnXml 属性
|
||||||
|
}
|
||||||
|
xmlString.value = data.bpmnXml
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
<style lang="scss">
|
||||||
|
.process-panel__container {
|
||||||
|
position: absolute;
|
||||||
|
top: 90px;
|
||||||
|
right: 60px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
155
src/views/pqs/process/definition/index.vue
Normal file
155
src/views/pqs/process/definition/index.vue
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
<!--业务用户管理界面-->
|
||||||
|
<template>
|
||||||
|
<div class="default-main">
|
||||||
|
<div v-show="addedShow">
|
||||||
|
<TableHeader>
|
||||||
|
<template v-slot:select>
|
||||||
|
<el-form-item label="筛选数据">
|
||||||
|
<el-input v-model="tableStore.table.params.name" clearable placeholder="筛选数据" />
|
||||||
|
</el-form-item>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-slot:operation>
|
||||||
|
<el-button type="primary" @click="add" class="ml10" icon="el-icon-Plus">新增</el-button>
|
||||||
|
</template>
|
||||||
|
</TableHeader>
|
||||||
|
<!--表格-->
|
||||||
|
<Table ref="tableRef"></Table>
|
||||||
|
</div>
|
||||||
|
<!-- 设计流程 -->
|
||||||
|
<editor v-if="!addedShow" :model="model" @quit="addedShow = true"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import TableStore from '@/utils/tableStore'
|
||||||
|
import Table from '@/components/table/index.vue'
|
||||||
|
import TableHeader from '@/components/table/header/index.vue'
|
||||||
|
import { onMounted, provide, ref } from 'vue'
|
||||||
|
|
||||||
|
import editor from './editor/index.vue';
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'harmonic-boot/reate/word'
|
||||||
|
})
|
||||||
|
|
||||||
|
const tableRef = ref()
|
||||||
|
const model = ref({})
|
||||||
|
const addedShow = ref(true)
|
||||||
|
|
||||||
|
const tableStore = new TableStore({
|
||||||
|
url: '/process-boot/flowable/definition/list',
|
||||||
|
method: 'GET',
|
||||||
|
column: [
|
||||||
|
{
|
||||||
|
title: '序号',
|
||||||
|
width: 60,
|
||||||
|
formatter: (row: any) => {
|
||||||
|
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ title: '流程标识', field: 'flowKey' },
|
||||||
|
{ title: '流程分类', field: 'category' },
|
||||||
|
{ title: '流程名称', field: 'name' },
|
||||||
|
|
||||||
|
{
|
||||||
|
title: '业务功能',
|
||||||
|
field: 'formId',
|
||||||
|
formatter: (row: any) => {
|
||||||
|
return row.cellValue == 1 ? '谐波普测计划' : row.cellValue == 2 ? '干扰源用户管理' : '暂无表单'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ title: '流程版本(V)', field: 'version' },
|
||||||
|
|
||||||
|
{
|
||||||
|
title: '状态',
|
||||||
|
field: 'suspensionState',
|
||||||
|
render: 'tag',
|
||||||
|
custom: {
|
||||||
|
0: '',
|
||||||
|
1: 'success',
|
||||||
|
2: 'warning'
|
||||||
|
},
|
||||||
|
replaceValue: {
|
||||||
|
0: '',
|
||||||
|
1: '激活',
|
||||||
|
2: '挂起'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ title: '部署时间', field: 'deploymentTime' },
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
width: '180',
|
||||||
|
render: 'buttons',
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
name: 'edit',
|
||||||
|
title: '设计',
|
||||||
|
type: 'primary',
|
||||||
|
icon: 'el-icon-Plus',
|
||||||
|
render: 'basicButton',
|
||||||
|
click: row => {
|
||||||
|
model.value = row
|
||||||
|
addedShow.value = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'edit',
|
||||||
|
title: '配置主表单',
|
||||||
|
type: 'primary',
|
||||||
|
icon: 'el-icon-Plus',
|
||||||
|
render: 'basicButton',
|
||||||
|
click: async row => {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'edit',
|
||||||
|
title: '挂起',
|
||||||
|
type: 'primary',
|
||||||
|
icon: 'el-icon-Plus',
|
||||||
|
render: 'basicButton',
|
||||||
|
click: async row => {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'edit',
|
||||||
|
title: '激活',
|
||||||
|
type: 'primary',
|
||||||
|
icon: 'el-icon-Plus',
|
||||||
|
render: 'basicButton',
|
||||||
|
click: async row => {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'del',
|
||||||
|
title: '删除',
|
||||||
|
type: 'danger',
|
||||||
|
icon: 'el-icon-Delete',
|
||||||
|
render: 'confirmButton',
|
||||||
|
|
||||||
|
popconfirm: {
|
||||||
|
confirmButtonText: '确认',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
confirmButtonType: 'danger',
|
||||||
|
title: '确定删除吗?'
|
||||||
|
},
|
||||||
|
click: row => {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
beforeSearchFun: () => {
|
||||||
|
tableStore.table.params.beginTime = tableStore.table.params.startTime
|
||||||
|
tableStore.table.params.deptId = tableStore.table.params.deptIndex
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// 新增
|
||||||
|
const add = () => {
|
||||||
|
model.value = {}
|
||||||
|
addedShow.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 加载数据
|
||||||
|
tableStore.index()
|
||||||
|
})
|
||||||
|
tableStore.table.params.name = ''
|
||||||
|
provide('tableStore', tableStore)
|
||||||
|
</script>
|
||||||
@@ -27,50 +27,81 @@
|
|||||||
|
|
||||||
<el-divider content-position="left" style="font-size: 18px; font-weight: bolder">填报流程</el-divider>
|
<el-divider content-position="left" style="font-size: 18px; font-weight: bolder">填报流程</el-divider>
|
||||||
|
|
||||||
<el-steps :active="active" finish-status="success" class="mb10" simple>
|
<el-tabs v-model="activeName" type="border-card">
|
||||||
<el-step v-for="(item, i) in stepTitle">
|
<el-tab-pane label="原因分析" name="0">
|
||||||
<template #title>
|
|
||||||
<span @click="step(i)">{{ item }}</span>
|
|
||||||
</template>
|
|
||||||
</el-step>
|
|
||||||
</el-steps>
|
|
||||||
|
|
||||||
<!-- 原因分析 0 -->
|
|
||||||
<process1
|
<process1
|
||||||
v-if="control == 0"
|
|
||||||
:addData="addData"
|
:addData="addData"
|
||||||
:List="List"
|
:List="List"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
ref="process0Ref"
|
ref="process0Ref"
|
||||||
@handleClose="handleClose"
|
@handleClose="handleClose"
|
||||||
/>
|
/>
|
||||||
<!-- 计划整改措施 1-->
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="计划整改措施" name="1">
|
||||||
<process2
|
<process2
|
||||||
v-if="control == 1"
|
|
||||||
:addData="addData"
|
:addData="addData"
|
||||||
:List="List"
|
:List="List"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
ref="process1Ref"
|
ref="process1Ref"
|
||||||
@handleClose="handleClose"
|
@handleClose="handleClose"
|
||||||
/>
|
/>
|
||||||
<!-- 实际采取措施 2 -->
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="实际采取措施" name="2">
|
||||||
<process3
|
<process3
|
||||||
v-if="control == 2"
|
|
||||||
:addData="addData"
|
:addData="addData"
|
||||||
:List="List"
|
:List="List"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
ref="process2Ref"
|
ref="process2Ref"
|
||||||
@handleClose="handleClose"
|
@handleClose="handleClose"
|
||||||
/>
|
/>
|
||||||
<!-- 成效分析 3 -->
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="成效分析" name="3">
|
||||||
<process4
|
<process4
|
||||||
v-if="control == 3"
|
|
||||||
:addData="addData"
|
:addData="addData"
|
||||||
:List="List"
|
:List="List"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
ref="process3Ref"
|
ref="process3Ref"
|
||||||
@handleClose="handleClose"
|
@handleClose="handleClose"
|
||||||
/>
|
/>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
|
||||||
|
<!-- 原因分析 0 -->
|
||||||
|
<!-- <process1
|
||||||
|
v-if="control == 0"
|
||||||
|
:addData="addData"
|
||||||
|
:List="List"
|
||||||
|
:disabled="disabled"
|
||||||
|
ref="process0Ref"
|
||||||
|
@handleClose="handleClose"
|
||||||
|
/> -->
|
||||||
|
<!-- 计划整改措施 1-->
|
||||||
|
<!-- <process2
|
||||||
|
v-if="control == 1"
|
||||||
|
:addData="addData"
|
||||||
|
:List="List"
|
||||||
|
:disabled="disabled"
|
||||||
|
ref="process1Ref"
|
||||||
|
@handleClose="handleClose"
|
||||||
|
/> -->
|
||||||
|
<!-- 实际采取措施 2 -->
|
||||||
|
<!-- <process3
|
||||||
|
v-if="control == 2"
|
||||||
|
:addData="addData"
|
||||||
|
:List="List"
|
||||||
|
:disabled="disabled"
|
||||||
|
ref="process2Ref"
|
||||||
|
@handleClose="handleClose"
|
||||||
|
/> -->
|
||||||
|
<!-- 成效分析 3 -->
|
||||||
|
<!-- <process4
|
||||||
|
v-if="control == 3"
|
||||||
|
:addData="addData"
|
||||||
|
:List="List"
|
||||||
|
:disabled="disabled"
|
||||||
|
ref="process3Ref"
|
||||||
|
@handleClose="handleClose"
|
||||||
|
/> -->
|
||||||
|
|
||||||
<!-- 填报 -->
|
<!-- 填报 -->
|
||||||
<div style="display: flex; justify-content: center; margin-top: 10px" v-show="!disabled">
|
<div style="display: flex; justify-content: center; margin-top: 10px" v-show="!disabled">
|
||||||
@@ -126,7 +157,7 @@ const List: any = ref({})
|
|||||||
const active = ref(4)
|
const active = ref(4)
|
||||||
const control = ref()
|
const control = ref()
|
||||||
const ruleFormRef = ref()
|
const ruleFormRef = ref()
|
||||||
const stepTitle = ['原因分析', '计划整改措施', '实际采取措施', '成效分析']
|
const activeName = ref('0')
|
||||||
const process0Ref = ref()
|
const process0Ref = ref()
|
||||||
const process1Ref = ref()
|
const process1Ref = ref()
|
||||||
const process2Ref = ref()
|
const process2Ref = ref()
|
||||||
@@ -257,4 +288,7 @@ defineExpose({ open })
|
|||||||
:deep(.el-upload-list__item) {
|
:deep(.el-upload-list__item) {
|
||||||
width: 400px;
|
width: 400px;
|
||||||
}
|
}
|
||||||
|
:deep(.el-tabs__content) {
|
||||||
|
height: auto !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -20,8 +20,7 @@ defineOptions({
|
|||||||
name: 'Processsupervision/electricitymanagement'
|
name: 'Processsupervision/electricitymanagement'
|
||||||
})
|
})
|
||||||
const activeName = ref('1')
|
const activeName = ref('1')
|
||||||
const Statistics = ref()
|
|
||||||
const compatibility = ref()
|
|
||||||
|
|
||||||
const layout = mainHeight(63) as any
|
const layout = mainHeight(63) as any
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
<planAdd ref="planAddRef" @onsubmit="tableStore.index()" />
|
<planAdd ref="planAddRef" @onsubmit="tableStore.index()" />
|
||||||
|
|
||||||
<!-- 选择审核人 -->
|
<!-- 选择审核人 -->
|
||||||
<el-dialog v-model="dialogVisible" title="审核" width="300" :before-close="handleClose">
|
<el-dialog draggable v-model="dialogVisible" title="审核" width="300" :before-close="handleClose">
|
||||||
选择审核人: <el-select v-model="auditUser" clearable placeholder="请选择计划状态">
|
选择审核人: <el-select v-model="auditUser" clearable placeholder="请选择计划状态">
|
||||||
<el-option v-for="item in auditList" :key="item.id" :label="item.name" :value="item.id"></el-option>
|
<el-option v-for="item in auditList" :key="item.id" :label="item.name" :value="item.id"></el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
@@ -40,7 +40,7 @@ import TableStore from '@/utils/tableStore'
|
|||||||
import Table from '@/components/table/index.vue'
|
import Table from '@/components/table/index.vue'
|
||||||
import TableHeader from '@/components/table/header/index.vue'
|
import TableHeader from '@/components/table/header/index.vue'
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
import { planStatus } from '@/api/process-boot/generalTest'
|
import { submitAuditUser } from '@/api/process-boot/generalTest'
|
||||||
import planAdd from './planAdd.vue'
|
import planAdd from './planAdd.vue'
|
||||||
import { useDictData } from '@/stores/dictData'
|
import { useDictData } from '@/stores/dictData'
|
||||||
|
|
||||||
@@ -190,13 +190,20 @@ const submit = () => {
|
|||||||
}
|
}
|
||||||
// 提交审核
|
// 提交审核
|
||||||
const planReviewFn = () => {
|
const planReviewFn = () => {
|
||||||
|
if(auditUser.value == ''){
|
||||||
|
return ElMessage.warning('请选择审核人')
|
||||||
|
}
|
||||||
let planNo: any = []
|
let planNo: any = []
|
||||||
tableStore.table.selection.forEach(item => {
|
tableStore.table.selection.forEach(item => {
|
||||||
if (item.status == 0 || item.status == 2) {
|
if (item.status == 0 || item.status == 2) {
|
||||||
planNo.push(item.planNo)
|
planNo.push(item.planNo)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
planStatus(planNo).then(res => {
|
submitAuditUser({
|
||||||
|
auditUser: auditUser.value,
|
||||||
|
planIds: planNo
|
||||||
|
}).then(res => {
|
||||||
|
dialogVisible.value = false
|
||||||
ElMessage.success('提交成功!')
|
ElMessage.success('提交成功!')
|
||||||
tableStore.index()
|
tableStore.index()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -19,9 +19,9 @@
|
|||||||
></el-input>
|
></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</template>
|
</template>
|
||||||
<template #operation>
|
<!-- <template #operation>
|
||||||
<el-button icon="el-icon-Stamp" type="primary">审核</el-button>
|
<el-button icon="el-icon-Stamp" type="primary">审核</el-button>
|
||||||
</template>
|
</template> -->
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
<Table ref="tableRef" />
|
<Table ref="tableRef" />
|
||||||
<!-- 审核 -->
|
<!-- 审核 -->
|
||||||
@@ -36,7 +36,7 @@ import TableHeader from '@/components/table/header/index.vue'
|
|||||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
import { mainHeight } from '@/utils/layout'
|
import { mainHeight } from '@/utils/layout'
|
||||||
import { useDictData } from '@/stores/dictData'
|
import { useDictData } from '@/stores/dictData'
|
||||||
import Audit from './audit.vue'
|
import Audit from '../undocumented/audit.vue'
|
||||||
|
|
||||||
const dictData = useDictData()
|
const dictData = useDictData()
|
||||||
const interferenceType = dictData.getBasicData('Interference_Source')
|
const interferenceType = dictData.getBasicData('Interference_Source')
|
||||||
@@ -82,7 +82,7 @@ const tableStore = new TableStore({
|
|||||||
|
|
||||||
beforeSearchFun: () => {
|
beforeSearchFun: () => {
|
||||||
tableStore.table.params.orgNo = tableStore.table.params.deptIndex
|
tableStore.table.params.orgNo = tableStore.table.params.deptIndex
|
||||||
tableStore.table.params.checkType = 1
|
// tableStore.table.params.checkType = 1
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
|
|
||||||
<!-- 上传 -->
|
<!-- 上传 -->
|
||||||
<el-dialog draggable :title="title" v-model="uploadConclusions" width="1500px" :before-close="cancel">
|
<el-dialog draggable :title="title" v-model="uploadConclusions" width="1500px" :before-close="cancel">
|
||||||
<el-divider content-position="left">基本信息</el-divider>
|
<el-divider content-position="left">基本信息</el-divider>
|
||||||
@@ -8,8 +9,7 @@
|
|||||||
:model="addForm"
|
:model="addForm"
|
||||||
label-width="auto"
|
label-width="auto"
|
||||||
:rules="rules"
|
:rules="rules"
|
||||||
class
|
:disabled="title == '未建档干扰源用户详情' || title == '入网评估报告审核'"
|
||||||
:disabled="title == '未建档干扰源用户详情'"
|
|
||||||
>
|
>
|
||||||
<el-form-item label="所属单位:">
|
<el-form-item label="所属单位:">
|
||||||
<Area v-model="addForm.orgNo" disabled />
|
<Area v-model="addForm.orgNo" disabled />
|
||||||
@@ -63,7 +63,7 @@
|
|||||||
<el-radio v-model="addForm.iIsOverLimit" :label="1">是</el-radio>
|
<el-radio v-model="addForm.iIsOverLimit" :label="1">是</el-radio>
|
||||||
<el-radio v-model="addForm.iIsOverLimit" :label="0">否</el-radio>
|
<el-radio v-model="addForm.iIsOverLimit" :label="0">否</el-radio>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<br v-if="show"/>
|
<br v-if="show" />
|
||||||
<el-form-item label="超标指标:" style="margin-top: 10px" prop="IOverLimitTarget" v-if="show">
|
<el-form-item label="超标指标:" style="margin-top: 10px" prop="IOverLimitTarget" v-if="show">
|
||||||
<el-checkbox-group v-model="addForm.IOverLimitTarget">
|
<el-checkbox-group v-model="addForm.IOverLimitTarget">
|
||||||
<el-checkbox v-for="(item, ind) in exceeded" :label="item.id">
|
<el-checkbox v-for="(item, ind) in exceeded" :label="item.id">
|
||||||
@@ -71,7 +71,7 @@
|
|||||||
</el-checkbox>
|
</el-checkbox>
|
||||||
</el-checkbox-group>
|
</el-checkbox-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<br v-if="show"/>
|
<br v-if="show" />
|
||||||
<el-form-item label="计划采取措施:" style="margin-top: 10px" prop="IPlanStep" v-if="show">
|
<el-form-item label="计划采取措施:" style="margin-top: 10px" prop="IPlanStep" v-if="show">
|
||||||
<el-select v-model="addForm.IPlanStep" placeholder="请选择">
|
<el-select v-model="addForm.IPlanStep" placeholder="请选择">
|
||||||
<el-option
|
<el-option
|
||||||
@@ -89,15 +89,45 @@
|
|||||||
:autosize="{ minRows: 2, maxRows: 4 }"
|
:autosize="{ minRows: 2, maxRows: 4 }"
|
||||||
placeholder="请输入入网详情"
|
placeholder="请输入入网详情"
|
||||||
type="textarea"
|
type="textarea"
|
||||||
style="width: 500px"
|
style="width: 400px"
|
||||||
|
></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form
|
||||||
|
:inline="true"
|
||||||
|
:model="addForm"
|
||||||
|
label-width="auto"
|
||||||
|
ref="form1Ref"
|
||||||
|
style="margin-left: 32px;"
|
||||||
|
:rules="rules"
|
||||||
|
v-if="title == '入网评估报告审核'"
|
||||||
|
>
|
||||||
|
<el-divider content-position="left" style="font-size: 18px; font-weight: bolder">审核意见</el-divider>
|
||||||
|
<el-form-item label="审核意见:" prop="checkComment">
|
||||||
|
<el-input
|
||||||
|
type="textarea"
|
||||||
|
style="width: 400px"
|
||||||
|
placeholder="请输入审核意见"
|
||||||
|
:autosize="{ minRows: 2, maxRows: 4 }"
|
||||||
|
v-model="addForm.checkComment"
|
||||||
></el-input>
|
></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<div style="display: flex; justify-content: center; margin-top: 30px" v-if="title != '未建档干扰源用户详情'">
|
</el-form>
|
||||||
|
<div
|
||||||
|
style="display: flex; justify-content: center; margin-top: 30px"
|
||||||
|
v-if="title != '未建档干扰源用户详情' && title != '入网评估报告审核'"
|
||||||
|
>
|
||||||
<el-button type="primary" class="ml20" @click="submit(1)">审核</el-button>
|
<el-button type="primary" class="ml20" @click="submit(1)">审核</el-button>
|
||||||
<el-button type="primary" class="ml20" @click="submit(2)">保存</el-button>
|
<el-button type="primary" class="ml20" @click="submit(2)">保存</el-button>
|
||||||
<el-button type="primary" class="ml20" @click="cancel">取消</el-button>
|
<el-button type="primary" class="ml20" @click="cancel">取消</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div style="display: flex; justify-content: center; margin-top: 30px" v-if="title == '入网评估报告审核'">
|
||||||
|
<el-button type="primary" class="ml20" @click="audit(1)">通过</el-button>
|
||||||
|
<el-button type="primary" class="ml20" @click="audit(0)">不通过</el-button>
|
||||||
|
<el-button type="primary" class="ml20" @click="cancel">取消</el-button>
|
||||||
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
@@ -106,7 +136,7 @@ import { useDictData } from '@/stores/dictData'
|
|||||||
import Area from '@/components/form/area/index.vue'
|
import Area from '@/components/form/area/index.vue'
|
||||||
import { UploadInstance, UploadProps, UploadRawFile, ElMessage, ElMessageBox } from 'element-plus'
|
import { UploadInstance, UploadProps, UploadRawFile, ElMessage, ElMessageBox } from 'element-plus'
|
||||||
import { genFileId } from 'element-plus'
|
import { genFileId } from 'element-plus'
|
||||||
import { uploadLoadTypeUserI, getLoadTypeUserById } from '@/api/process-boot/interference'
|
import { uploadLoadTypeUserI, getLoadTypeUserById, checkLoadTypeUserI } from '@/api/process-boot/interference'
|
||||||
const dictData = useDictData()
|
const dictData = useDictData()
|
||||||
|
|
||||||
const exceeded = dictData.getBasicData('Steady_Statis')
|
const exceeded = dictData.getBasicData('Steady_Statis')
|
||||||
@@ -134,10 +164,13 @@ const rules = {
|
|||||||
iIsOverLimit: [{ required: true, message: '请选择是否超标', trigger: 'change' }],
|
iIsOverLimit: [{ required: true, message: '请选择是否超标', trigger: 'change' }],
|
||||||
IDescription: [{ required: true, message: '请输入入网详情', trigger: 'blur' }],
|
IDescription: [{ required: true, message: '请输入入网详情', trigger: 'blur' }],
|
||||||
IPlanStep: [{ required: true, message: '请选择计划采取措施', trigger: 'change' }],
|
IPlanStep: [{ required: true, message: '请选择计划采取措施', trigger: 'change' }],
|
||||||
IOverLimitTarget: [{ required: true, message: '请选择超标指标', trigger: 'change' }]
|
IOverLimitTarget: [{ required: true, message: '请选择超标指标', trigger: 'change' }],
|
||||||
|
checkComment: [{ required: true, message: '请输入入网详情', trigger: 'blur' }]
|
||||||
}
|
}
|
||||||
const formRef = ref()
|
const formRef = ref()
|
||||||
|
const form1Ref = ref()
|
||||||
|
|
||||||
|
// 填报 审核
|
||||||
const submit = (flag: any) => {
|
const submit = (flag: any) => {
|
||||||
formRef.value.validate((valid: any) => {
|
formRef.value.validate((valid: any) => {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
@@ -165,6 +198,23 @@ const submit = (flag: any) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
// 入网审核
|
||||||
|
const audit = (flag: any) => {
|
||||||
|
form1Ref.value?.validate((valid: any) => {
|
||||||
|
if (valid) {
|
||||||
|
checkLoadTypeUserI({
|
||||||
|
checkComment: addForm.value.checkComment,
|
||||||
|
checkPerson: dictData.state.area[0].id,
|
||||||
|
checkResult: flag,
|
||||||
|
id: addForm.value.id
|
||||||
|
}).then((res: any) => {
|
||||||
|
ElMessage.success('操作成功')
|
||||||
|
cancel()
|
||||||
|
emit('onSubmit')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
const handleExceed: UploadProps['onExceed'] = files => {
|
const handleExceed: UploadProps['onExceed'] = files => {
|
||||||
upload.value!.clearFiles()
|
upload.value!.clearFiles()
|
||||||
const file = files[0] as UploadRawFile
|
const file = files[0] as UploadRawFile
|
||||||
@@ -191,8 +241,8 @@ const cancel = () => {
|
|||||||
|
|
||||||
const open = (text: string, row: any) => {
|
const open = (text: string, row: any) => {
|
||||||
title.value = text
|
title.value = text
|
||||||
|
|
||||||
getLoadTypeUserById({ id: row.id }).then((res: any) => {
|
getLoadTypeUserById({ id: row.id }).then((res: any) => {
|
||||||
|
uploadConclusions.value = true
|
||||||
addForm.value = {
|
addForm.value = {
|
||||||
id: res.data.id,
|
id: res.data.id,
|
||||||
orgNo: res.data.orgNo,
|
orgNo: res.data.orgNo,
|
||||||
@@ -207,9 +257,8 @@ const open = (text: string, row: any) => {
|
|||||||
ifilePathName: res.data.ifilePathName ? res.data.ifilePathName : '',
|
ifilePathName: res.data.ifilePathName ? res.data.ifilePathName : '',
|
||||||
ifile: res.data.ifile
|
ifile: res.data.ifile
|
||||||
}
|
}
|
||||||
|
res.data.iisOverLimit == 0 ? (show.value = false) : (show.value = true)
|
||||||
})
|
})
|
||||||
|
|
||||||
uploadConclusions.value = true
|
|
||||||
}
|
}
|
||||||
const changeOverLimit = (e: any) => {
|
const changeOverLimit = (e: any) => {
|
||||||
if (e.target.value == 0) {
|
if (e.target.value == 0) {
|
||||||
|
|||||||
224
src/views/pqs/supervise/retire/index.vue
Normal file
224
src/views/pqs/supervise/retire/index.vue
Normal file
@@ -0,0 +1,224 @@
|
|||||||
|
<template>
|
||||||
|
<div class="default-main">
|
||||||
|
<TableHeader area datePicker ref="TableHeaderRef">
|
||||||
|
<template #select>
|
||||||
|
<el-form-item label="问题来源">
|
||||||
|
<el-select v-model="tableStore.table.params.problemSources" clearable placeholder="请选择问题来源">
|
||||||
|
<el-option
|
||||||
|
v-for="item in problemData"
|
||||||
|
:key="item.code"
|
||||||
|
:label="item.name"
|
||||||
|
:value="item.code"
|
||||||
|
></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="填报进度">
|
||||||
|
<el-select v-model="tableStore.table.params.reportProcess" clearable placeholder="请选择填报进度">
|
||||||
|
<el-option
|
||||||
|
v-for="item in fillingProgress"
|
||||||
|
:key="item.code"
|
||||||
|
:label="item.name"
|
||||||
|
:value="item.code"
|
||||||
|
></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</template>
|
||||||
|
<template #operation>
|
||||||
|
<el-button icon="el-icon-Plus" type="primary" @click="add">新增</el-button>
|
||||||
|
</template>
|
||||||
|
</TableHeader>
|
||||||
|
<Table ref="tableRef" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted, provide, nextTick } from 'vue'
|
||||||
|
import TableStore from '@/utils/tableStore'
|
||||||
|
import Table from '@/components/table/index.vue'
|
||||||
|
import TableHeader from '@/components/table/header/index.vue'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import { deleteIssues, archive } from '@/api/process-boot/electricitymanagement'
|
||||||
|
import { useDictData } from '@/stores/dictData'
|
||||||
|
const dictData = useDictData()
|
||||||
|
const FillingRef = ref()
|
||||||
|
|
||||||
|
const showNewlyAdded = ref(false)
|
||||||
|
const dialogVisible = ref(false)
|
||||||
|
const recordingRef = ref(false)
|
||||||
|
const TableHeaderRef = ref()
|
||||||
|
const detailRef = ref()
|
||||||
|
const problemData = dictData.getBasicData('Problem_Sources')
|
||||||
|
const fillingProgress = dictData.getBasicData('Fill_Progress')
|
||||||
|
const auditStatus = dictData.getBasicData('Audit_Status')
|
||||||
|
defineOptions({
|
||||||
|
name: '/Processsupervision/retire'
|
||||||
|
})
|
||||||
|
const tableStore: any = new TableStore({
|
||||||
|
url: '/process-boot/electricityQuality/getIssues',
|
||||||
|
method: 'POST',
|
||||||
|
column: [
|
||||||
|
// { width: '60', type: 'checkbox' },
|
||||||
|
{
|
||||||
|
field: 'index',
|
||||||
|
title: '序号',
|
||||||
|
width: '60',
|
||||||
|
formatter: (row: any) => {
|
||||||
|
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ field: 'orgName', title: '所属单位' },
|
||||||
|
{
|
||||||
|
field: 'problemSources',
|
||||||
|
title: '问题来源'
|
||||||
|
},
|
||||||
|
{ field: 'powerQualityProblemNo', title: '问题编号' },
|
||||||
|
{ field: 'problemName', title: '问题名称' },
|
||||||
|
{ field: 'dataDate', title: '问题新建时间' },
|
||||||
|
{
|
||||||
|
field: 'reportProcess',
|
||||||
|
title: '填报进度',
|
||||||
|
formatter: (row: any) => {
|
||||||
|
return fillingProgress.filter(item => item.code == row.cellValue)[0]?.name
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'reportProcessStatus',
|
||||||
|
title: '审核状态',
|
||||||
|
formatter: (row: any) => {
|
||||||
|
return auditStatus.filter(item => item.code == row.cellValue)[0]?.name
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
width: '180',
|
||||||
|
render: 'buttons',
|
||||||
|
buttons: [
|
||||||
|
{
|
||||||
|
name: 'edit',
|
||||||
|
title: '查看',
|
||||||
|
type: 'primary',
|
||||||
|
disabled: row => {
|
||||||
|
return row.reportProcessStatus == 'Init'
|
||||||
|
},
|
||||||
|
icon: 'el-icon-Plus',
|
||||||
|
render: 'basicButton',
|
||||||
|
click: async row => {
|
||||||
|
detailRef.value.open(row)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'edit',
|
||||||
|
title: '填报',
|
||||||
|
disabled: row => {
|
||||||
|
return (
|
||||||
|
row.reportProcessStatus == 'Auditt' ||
|
||||||
|
(row.reportProcess == 'Insights' && row.reportProcessStatus == 'Success') ||
|
||||||
|
row.reportProcess == 'Archived'
|
||||||
|
)
|
||||||
|
},
|
||||||
|
type: 'primary',
|
||||||
|
icon: 'el-icon-Plus',
|
||||||
|
render: 'basicButton',
|
||||||
|
click: row => {
|
||||||
|
dialogVisible.value = true
|
||||||
|
setTimeout(() => {
|
||||||
|
FillingRef.value.open(row)
|
||||||
|
}, 10)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'edit',
|
||||||
|
title: '归档',
|
||||||
|
disabled: row => {
|
||||||
|
return !(row.reportProcess == 'Insights' && row.reportProcessStatus == 'Success')
|
||||||
|
},
|
||||||
|
type: 'primary',
|
||||||
|
icon: 'el-icon-SuccessFilled',
|
||||||
|
render: 'basicButton',
|
||||||
|
popconfirm: {
|
||||||
|
confirmButtonText: '确认',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
confirmButtonType: 'danger',
|
||||||
|
title: '确定归档?'
|
||||||
|
},
|
||||||
|
click: row => {
|
||||||
|
archive(row.powerQualityProblemNo).then(() => {
|
||||||
|
ElMessage.success('归档成功!')
|
||||||
|
tableStore.index()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'edit',
|
||||||
|
title: '审核记录',
|
||||||
|
type: 'primary',
|
||||||
|
disabled: row => {
|
||||||
|
return row.reportProcessStatus == 'Init'
|
||||||
|
},
|
||||||
|
icon: 'el-icon-PieChart',
|
||||||
|
render: 'basicButton',
|
||||||
|
click: row => {
|
||||||
|
recordingRef.value.open(row)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'del',
|
||||||
|
text: '删除',
|
||||||
|
type: 'danger',
|
||||||
|
icon: 'el-icon-Delete',
|
||||||
|
render: 'confirmButton',
|
||||||
|
popconfirm: {
|
||||||
|
confirmButtonText: '确认',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
confirmButtonType: 'danger',
|
||||||
|
title: '确定删除?'
|
||||||
|
},
|
||||||
|
disabled: row => {
|
||||||
|
return row.reportProcess == 'Archived'
|
||||||
|
},
|
||||||
|
click: row => {
|
||||||
|
deleteIssues(row.powerQualityProblemNo).then(() => {
|
||||||
|
ElMessage.success('删除成功')
|
||||||
|
tableStore.index()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
beforeSearchFun: () => {
|
||||||
|
tableStore.table.params.orgNo = tableStore.table.params.deptIndex
|
||||||
|
tableStore.table.params.dataDate = tableStore.table.params.searchBeginTime
|
||||||
|
tableStore.table.params.dataType = TableHeaderRef.value.datePickerRef.interval
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
tableStore.table.params.problemName = ''
|
||||||
|
tableStore.table.params.problemSources = ''
|
||||||
|
tableStore.table.params.reportProcess = ''
|
||||||
|
tableStore.table.params.reportProcessStatus = ''
|
||||||
|
|
||||||
|
provide('tableStore', tableStore)
|
||||||
|
onMounted(() => {
|
||||||
|
TableHeaderRef.value.setDatePicker([
|
||||||
|
{ label: '年', value: 1 },
|
||||||
|
{ label: '季', value: 2 },
|
||||||
|
{ label: '月', value: 3 }
|
||||||
|
])
|
||||||
|
|
||||||
|
tableStore.index()
|
||||||
|
})
|
||||||
|
// 新增
|
||||||
|
const add = () => {
|
||||||
|
showNewlyAdded.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭弹框
|
||||||
|
const handleClose = () => {
|
||||||
|
showNewlyAdded.value = false
|
||||||
|
}
|
||||||
|
// 关闭 填报
|
||||||
|
const beforeClose = () => {
|
||||||
|
dialogVisible.value = false
|
||||||
|
tableStore.index()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="containerBox" style="position: relative">
|
|
||||||
<div id="container" style="width: calc(100vw - 750px); height: calc(100vh - 150px)"></div>
|
|
||||||
<div id="js-properties-panel" class="panel"></div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { onMounted, markRaw } from 'vue'
|
|
||||||
|
|
||||||
import 'bpmn-js/dist/assets/diagram-js.css' // 左边工具栏以及编辑节点的样式
|
|
||||||
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css'
|
|
||||||
import BpmnModeler from 'bpmn-js/lib/Modeler'
|
|
||||||
// bpmn-js-properties-panel相关
|
|
||||||
import 'bpmn-js-properties-panel/dist/assets/bpmn-js-properties-panel.css'
|
|
||||||
import propertiesPanelModule from 'bpmn-js-properties-panel'
|
|
||||||
import propertiesProviderModule from 'bpmn-js-properties-panel/lib/provider/camunda'
|
|
||||||
import camundaModdleDescriptor from 'camunda-bpmn-moddle/resources/camunda'
|
|
||||||
import translate from '@/assets/bpmn/translate'
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
const containerEl = document.getElementById('container')
|
|
||||||
const bpmnModeler = markRaw(
|
|
||||||
new BpmnModeler({
|
|
||||||
container: containerEl,
|
|
||||||
// 添加控制板
|
|
||||||
propertiesPanel: {
|
|
||||||
parent: '#js-properties-panel'
|
|
||||||
},
|
|
||||||
// 右侧属性面板
|
|
||||||
additionalModules: [propertiesPanelModule, propertiesProviderModule],
|
|
||||||
moddleExtensions: {
|
|
||||||
camunda: camundaModdleDescriptor
|
|
||||||
}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
bpmnModeler.createDiagram(() => {
|
|
||||||
bpmnModeler.get('canvas').zoom('fit-viewport')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
var customTranslateModule = {
|
|
||||||
translate: ['value', translate]
|
|
||||||
}
|
|
||||||
|
|
||||||
const bpmnModeler = new BpmnModeler({
|
|
||||||
additionalModules: [customTranslateModule]
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.containerBox {
|
|
||||||
height: calc(100vh - 160px);
|
|
||||||
margin-top: 30px;
|
|
||||||
}
|
|
||||||
.containerBox #container {
|
|
||||||
height: calc(100vh - 160px);
|
|
||||||
border: 1px solid rgb(121, 121, 121);
|
|
||||||
}
|
|
||||||
.bpp-properties-panel [type='text'] {
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
.panel {
|
|
||||||
width: 400px;
|
|
||||||
position: absolute;
|
|
||||||
top: 1px;
|
|
||||||
right: 1px;
|
|
||||||
height: 100%;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
/* 右下角logo */
|
|
||||||
.bjs-powered-by {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="default-main" :style="layout">
|
|
||||||
<!-- <work /> -->
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { onMounted, reactive, ref, provide } from 'vue'
|
|
||||||
// import work from './work.vue'
|
|
||||||
|
|
||||||
import { mainHeight } from '@/utils/layout'
|
|
||||||
defineOptions({
|
|
||||||
name: 'Workflow/Designer/tools'
|
|
||||||
})
|
|
||||||
|
|
||||||
const layout = mainHeight(20)
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
import type { XmlMetaInfo } from "@/api/activiti/modeler";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* bpmn2.0标准
|
|
||||||
* activiti标准转化为camunda标准
|
|
||||||
* @param xml
|
|
||||||
*/
|
|
||||||
export const activitiToCamundaXml = (xml: string, metaInfo?: XmlMetaInfo) => {
|
|
||||||
//替换activiti标准
|
|
||||||
xml = xml.replace("xmlns:activiti", "xmlns:camunda");
|
|
||||||
xml = xml.replace("http://activiti.org/bpmn", "http://camunda.org/schema/1.0/bpmn");
|
|
||||||
xml = xml.replaceAll("activiti:", "camunda:");
|
|
||||||
//显示版本信息
|
|
||||||
if (metaInfo && metaInfo.version) {
|
|
||||||
xml.replace("<bpmn2:process", `<bpmn:process camunda:versionTag="${metaInfo.version}"`);
|
|
||||||
}
|
|
||||||
return xml;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* bpmn2.0标准
|
|
||||||
* camunda标准转化为activiti标准
|
|
||||||
* @param xml
|
|
||||||
*/
|
|
||||||
export const camundaToActivitiXml = (xml: string) => {
|
|
||||||
//追加头部信息
|
|
||||||
xml = xml.replace(
|
|
||||||
"<bpmn2:definitions",
|
|
||||||
`<bpmn2:definitions typeLanguage="http://www.w3.org/2001/XMLSchema"
|
|
||||||
expressionLanguage="http://www.w3.org/1999/XPath"`
|
|
||||||
);
|
|
||||||
//删除版本号
|
|
||||||
// xml = xml.replace(/camunda:versionTag="*"/, "");
|
|
||||||
//替换camunda标准
|
|
||||||
xml = xml.replace("xmlns:camunda", "xmlns:activiti");
|
|
||||||
xml = xml.replace("http://camunda.org/schema/1.0/bpmn", "http://activiti.org/bpmn");
|
|
||||||
xml = xml.replaceAll("camunda:", "activiti:");
|
|
||||||
return xml;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* bpmn2.0标准
|
|
||||||
* 初始化一个camunda标准的xml
|
|
||||||
* @param {XmlMetaInfo} metaInfo
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
const createBpmnXml = ({ key, name, description, version }: XmlMetaInfo) => {
|
|
||||||
const keyStr = key ? key : "Process_1";
|
|
||||||
const nameStr = name ? name : "";
|
|
||||||
const descriptionStr = description ? `<bpmn2:documentation>${description}</bpmn2:documentation>` : "";
|
|
||||||
const versionStr = version ? version : 1;
|
|
||||||
|
|
||||||
const xmlStr = `<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<bpmn2:definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
|
|
||||||
xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL"
|
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xmlns:camunda="http://camunda.org/schema/1.0/bpmn"
|
|
||||||
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
|
|
||||||
xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC"
|
|
||||||
xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI"
|
|
||||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
|
||||||
targetNamespace="http://www.activiti.org/test">
|
|
||||||
<bpmn2:process id="${keyStr}" name="${nameStr}" isExecutable="true" camunda:versionTag="${versionStr}" >
|
|
||||||
${descriptionStr}
|
|
||||||
</bpmn2:process>
|
|
||||||
<bpmndi:BPMNDiagram id="BpmnDiagram_1">
|
|
||||||
<bpmndi:BPMNPlane id="BpmnPlane_1" bpmnElement="${keyStr}" />
|
|
||||||
</bpmndi:BPMNDiagram>
|
|
||||||
</bpmn2:definitions>`;
|
|
||||||
return xmlStr;
|
|
||||||
};
|
|
||||||
export default createBpmnXml;
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
|
|
||||||
<div> </div>
|
<div>代办 </div>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
<script setup lang='ts'>
|
<script setup lang='ts'>
|
||||||
11
src/views/pqs/task/done/index.vue
Normal file
11
src/views/pqs/task/done/index.vue
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<template>
|
||||||
|
|
||||||
|
<div> 已办</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
<script setup lang='ts'>
|
||||||
|
import { ref, reactive } from 'vue'
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
103
src/views/pqs/task/myTask/index.vue
Normal file
103
src/views/pqs/task/myTask/index.vue
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
<!--业务用户管理界面-->
|
||||||
|
<template>
|
||||||
|
<div class="default-main">
|
||||||
|
<TableHeader >
|
||||||
|
<template v-slot:select>
|
||||||
|
<el-form-item label="筛选数据">
|
||||||
|
<el-input v-model="tableStore.table.params.name" clearable placeholder="筛选数据" />
|
||||||
|
</el-form-item>
|
||||||
|
</template>
|
||||||
|
<template v-slot:operation>
|
||||||
|
<el-button type="primary" @click="exportEvent" class="ml10" icon="el-icon-Download">导出</el-button>
|
||||||
|
</template>
|
||||||
|
</TableHeader>
|
||||||
|
<!--表格-->
|
||||||
|
<Table ref="tableRef"></Table>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import TableStore from '@/utils/tableStore'
|
||||||
|
import Table from '@/components/table/index.vue'
|
||||||
|
import TableHeader from '@/components/table/header/index.vue'
|
||||||
|
import { onMounted, provide, ref } from 'vue'
|
||||||
|
import { useDictData } from '@/stores/dictData'
|
||||||
|
import { pageTable } from '@/api/harmonic-boot/luckyexcel'
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'harmonic-boot/reate/word'
|
||||||
|
})
|
||||||
|
const dictData = useDictData()
|
||||||
|
//区域联级选择
|
||||||
|
const industry = dictData.getBasicData('Interference_Source')
|
||||||
|
//用户信息弹出框
|
||||||
|
const tableRef = ref()
|
||||||
|
|
||||||
|
const tableStore = new TableStore({
|
||||||
|
url: '/process-boot/flowable/task/todoList',
|
||||||
|
method: 'GET',
|
||||||
|
column: [
|
||||||
|
{
|
||||||
|
title: '序号',
|
||||||
|
width: 60,
|
||||||
|
formatter: (row: any) => {
|
||||||
|
return (tableStore.table.params.pageNum - 1) * tableStore.table.params.pageSize + row.rowIndex + 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ title: '变电站', field: 'subName', width: 200 },
|
||||||
|
{ title: '监测点名称', field: 'lineName', width: 200 },
|
||||||
|
{
|
||||||
|
title: '行业类型',
|
||||||
|
field: 'businessType',
|
||||||
|
formatter: (row: any) => {
|
||||||
|
return industry.find((item: any) => item.id == row.cellValue)?.name || '/'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '分类等级',
|
||||||
|
field: 'calssificationGrade',
|
||||||
|
formatter: (row: any) => {
|
||||||
|
return row.cellValue || '/'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ title: '电压等级', field: 'voltageScale' },
|
||||||
|
{
|
||||||
|
title: '上级变电站',
|
||||||
|
field: 'superiorsSubstation',
|
||||||
|
formatter: (row: any) => {
|
||||||
|
return row.cellValue || '/'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
beforeSearchFun: () => {
|
||||||
|
tableStore.table.params.beginTime = tableStore.table.params.startTime
|
||||||
|
tableStore.table.params.deptId = tableStore.table.params.deptIndex
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 加载数据
|
||||||
|
tableStore.index()
|
||||||
|
})
|
||||||
|
tableStore.table.params.name = ''
|
||||||
|
provide('tableStore', tableStore)
|
||||||
|
// 导出
|
||||||
|
const exportEvent = () => {
|
||||||
|
let form = JSON.parse(JSON.stringify(tableStore.table.params))
|
||||||
|
form.pageNum = 1
|
||||||
|
form.pageSize = tableStore.table.total
|
||||||
|
pageTable(form).then(res => {
|
||||||
|
tableRef.value.getRef().exportData({
|
||||||
|
filename: '合格率报告', // 文件名字
|
||||||
|
sheetName: 'Sheet1',
|
||||||
|
type: 'xlsx', //导出文件类型 xlsx 和 csv
|
||||||
|
useStyle: true,
|
||||||
|
data: res.data.records, // 数据源 // 过滤那个字段导出
|
||||||
|
columnFilterMethod: function (column: any) {
|
||||||
|
return !(column.$columnIndex === 0)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -28,7 +28,7 @@ const size = ref(0)
|
|||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const dom = document.getElementById('navigation-splitpanes')
|
const dom = document.getElementById('navigation-splitpanes')
|
||||||
if (dom) {
|
if (dom) {
|
||||||
size.value = Math.round((280 / dom.offsetHeight) * 100)
|
size.value = Math.round((180 / dom.offsetHeight) * 100)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const handleNodeClick = (data: any, node: any) => {
|
const handleNodeClick = (data: any, node: any) => {
|
||||||
|
|||||||
@@ -182,7 +182,7 @@ provide('tableStore', tableStore)
|
|||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const dom = document.getElementById('navigation-splitpanes')
|
const dom = document.getElementById('navigation-splitpanes')
|
||||||
if (dom) {
|
if (dom) {
|
||||||
size.value = Math.round((280 / dom.offsetHeight) * 100)
|
size.value = Math.round((180 / dom.offsetHeight) * 100)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
getList({
|
getList({
|
||||||
|
|||||||
Reference in New Issue
Block a user