初始化项目,自 v1.7.1 版本开始

This commit is contained in:
YunaiV
2023-02-11 00:44:00 +08:00
parent 11161afc1a
commit 56f3017baa
548 changed files with 52096 additions and 61 deletions

View File

@ -0,0 +1,698 @@
<template>
<div class="my-process-designer">
<div class="my-process-designer__header">
<slot name="control-header"></slot>
<template v-if="!$slots['control-header']">
<ElButtonGroup key="file-control">
<XButton preIcon="ep:folder-opened" title="打开文件" @click="refFile.click()" />
<el-tooltip effect="light" placement="bottom">
<template #content>
<div style="color: #409eff">
<!-- <el-button link @click="downloadProcessAsXml()">下载为XML文件</el-button> -->
<XTextButton title="下载为XML文件" @click="downloadProcessAsXml()" />
<br />
<!-- <el-button link @click="downloadProcessAsSvg()">下载为SVG文件</el-button> -->
<XTextButton title="下载为SVG文件" @click="downloadProcessAsSvg()" />
<br />
<!-- <el-button link @click="downloadProcessAsBpmn()">下载为BPMN文件</el-button> -->
<XTextButton title="下载为BPMN文件" @click="downloadProcessAsBpmn()" />
</div>
</template>
<XButton title="下载文件" preIcon="ep:download" />
</el-tooltip>
<el-tooltip effect="light">
<XButton preIcon="ep:view" title="浏览" />
<template #content>
<!-- <el-button link @click="previewProcessXML">预览XML</el-button> -->
<XTextButton title="预览XML" @click="previewProcessXML" />
<br />
<!-- <el-button link @click="previewProcessJson">预览JSON</el-button> -->
<XTextButton title="预览JSON" @click="previewProcessJson" />
</template>
</el-tooltip>
<el-tooltip
v-if="props.simulation"
effect="light"
:content="simulationStatus ? '退出模拟' : '开启模拟'"
>
<XButton preIcon="ep:cpu" title="模拟" @click="processSimulation" />
</el-tooltip>
</ElButtonGroup>
<ElButtonGroup key="align-control">
<el-tooltip effect="light" content="向左对齐">
<!-- <el-button
class="align align-left"
icon="el-icon-s-data"
@click="elementsAlign('left')"
/> -->
<XButton
preIcon="fa:align-left"
class="align align-bottom"
@click="elementsAlign('left')"
/>
</el-tooltip>
<el-tooltip effect="light" content="向右对齐">
<!-- <el-button
class="align align-right"
icon="el-icon-s-data"
@click="elementsAlign('right')"
/> -->
<XButton
preIcon="fa:align-left"
class="align align-top"
@click="elementsAlign('right')"
/>
</el-tooltip>
<el-tooltip effect="light" content="向上对齐">
<!-- <el-button
class="align align-top"
icon="el-icon-s-data"
@click="elementsAlign('top')"
/> -->
<XButton
preIcon="fa:align-left"
class="align align-left"
@click="elementsAlign('top')"
/>
</el-tooltip>
<el-tooltip effect="light" content="向下对齐">
<!-- <el-button
class="align align-bottom"
icon="el-icon-s-data"
@click="elementsAlign('bottom')"
/> -->
<XButton
preIcon="fa:align-left"
class="align align-right"
@click="elementsAlign('bottom')"
/>
</el-tooltip>
<el-tooltip effect="light" content="水平居中">
<!-- <el-button
class="align align-center"
icon="el-icon-s-data"
@click="elementsAlign('center')"
/> -->
<!-- class="align align-center" -->
<XButton
preIcon="fa:align-left"
class="align align-center"
@click="elementsAlign('center')"
/>
</el-tooltip>
<el-tooltip effect="light" content="垂直居中">
<!-- <el-button
class="align align-middle"
icon="el-icon-s-data"
@click="elementsAlign('middle')"
/> -->
<XButton
preIcon="fa:align-left"
class="align align-middle"
@click="elementsAlign('middle')"
/>
</el-tooltip>
</ElButtonGroup>
<ElButtonGroup key="scale-control">
<el-tooltip effect="light" content="缩小视图">
<!-- <el-button
:disabled="defaultZoom < 0.2"
icon="el-icon-zoom-out"
@click="processZoomOut()"
/> -->
<XButton
preIcon="ep:zoom-out"
@click="processZoomOut()"
:disabled="defaultZoom < 0.2"
/>
</el-tooltip>
<el-button>{{ Math.floor(defaultZoom * 10 * 10) + '%' }}</el-button>
<el-tooltip effect="light" content="放大视图">
<!-- <el-button
:disabled="defaultZoom > 4"
icon="el-icon-zoom-in"
@click="processZoomIn()"
/> -->
<XButton preIcon="ep:zoom-in" @click="processZoomIn()" :disabled="defaultZoom > 4" />
</el-tooltip>
<el-tooltip effect="light" content="重置视图并居中">
<!-- <el-button icon="el-icon-c-scale-to-original" @click="processReZoom()" /> -->
<XButton preIcon="ep:scale-to-original" @click="processReZoom()" />
</el-tooltip>
</ElButtonGroup>
<ElButtonGroup key="stack-control">
<el-tooltip effect="light" content="撤销">
<!-- <el-button :disabled="!revocable" icon="el-icon-refresh-left" @click="processUndo()" /> -->
<XButton preIcon="ep:refresh-left" @click="processUndo()" :disabled="!revocable" />
</el-tooltip>
<el-tooltip effect="light" content="恢复">
<!-- <el-button
:disabled="!recoverable"
icon="el-icon-refresh-right"
@click="processRedo()"
/> -->
<XButton preIcon="ep:refresh-right" @click="processRedo()" :disabled="!recoverable" />
</el-tooltip>
<el-tooltip effect="light" content="重新绘制">
<!-- <el-button icon="el-icon-refresh" @click="processRestart" /> -->
<XButton preIcon="ep:refresh" @click="processRestart()" />
</el-tooltip>
</ElButtonGroup>
<XButton
preIcon="ep:plus"
title="保存模型"
@click="processSave"
:type="props.headerButtonType"
:disabled="simulationStatus"
/>
</template>
<!-- 用于打开本地文件-->
<input
type="file"
id="files"
ref="refFile"
style="display: none"
accept=".xml, .bpmn"
@change="importLocalFile"
/>
</div>
<div class="my-process-designer__container">
<div
class="my-process-designer__canvas"
ref="bpmnCanvas"
id="bpmnCanvas"
style="width: 1680px; height: 800px"
></div>
<!-- <div id="js-properties-panel" class="panel"></div> -->
<!-- <div class="my-process-designer__canvas" ref="bpmn-canvas"></div> -->
</div>
<XModal title="预览" width="80%" height="90%" v-model="previewModelVisible" destroy-on-close>
<!-- append-to-body -->
<pre v-highlight>
<code class="hljs">
<!-- 高亮代码块 -->
{{ previewResult }}
</code>
</pre>
<!-- <pre>
<code class="hljs" v-html="highlightedCode(previewType, previewResult)"></code>
</pre> -->
</XModal>
</div>
</template>
<script setup lang="ts" name="MyProcessDesigner">
// 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 { ElMessage, ElMessageBox } from 'element-plus'
import BpmnModeler from 'bpmn-js/lib/Modeler'
import DefaultEmptyXML from './plugins/defaultEmpty'
// 翻译方法
import customTranslate from './plugins/translate/customTranslate'
import translationsCN from './plugins/translate/zh'
// 模拟流转流程
import tokenSimulation from 'bpmn-js-token-simulation'
// 标签解析构建器
// 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: ''
// })
const bpmnCanvas = ref()
const refFile = ref()
const emit = defineEmits([
'destroy',
'init-finished',
'save',
'commandStack-changed',
'input',
'change',
'canvas-viewbox-changed',
// eventName.name
'element-click'
])
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)
} 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 || '&nbsp;'
// }
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>

View File

@ -0,0 +1,595 @@
<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 setup lang="ts" name="MyProcessViewer">
import BpmnViewer from 'bpmn-js/lib/Viewer'
import DefaultEmptyXML from './plugins/defaultEmpty'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
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] // 获得最后一个任务
// 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
}
// 高亮任务
canvas.addMarker(n.id, getResultCss(task.result))
// 如果非通过,就不走后面的线条了
if (task.result !== 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') {
// 开始节点
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.result === 1) {
return
}
canvas.addMarker(n.id, getResultCss(processInstance.value.result))
} 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))
})
}
}
})
}
const getActivityHighlightCss = (activity) => {
return activity.endTime ? 'highlight' : 'highlight-todo'
}
const getResultCss = (result) => {
if (result === 1) {
// 审批中
return 'highlight-todo'
} else if (result === 2) {
// 已通过
return 'highlight'
} else if (result === 3) {
// 不通过
return 'highlight-reject'
} else if (result === 4) {
// 已取消
return 'highlight-cancel'
}
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>创建时间:${parseTime(processInstance.value.createTime)}`
} else if (element.value.type === 'bpmn:UserTask') {
// debugger
let task = taskList.value.find((m) => m.id === activity.taskId) // 找到活动对应的 taskId
if (!task) {
return
}
let optionData = getIntDictOptions(DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT)
let dataResult = ''
optionData.forEach((element) => {
if (element.value == task.result) {
dataResult = element.label
}
})
html = `<p>审批人:${task.assigneeUser.nickname}</p>
<p>部门:${task.assigneeUser.deptName}</p>
<p>结果:${dataResult}</p>
<p>创建时间:${parseTime(task.createTime)}</p>`
// html = `<p>审批人:${task.assigneeUser.nickname}</p>
// <p>部门:${task.assigneeUser.deptName}</p>
// <p>结果:${getIntDictOptions(
// DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT,
// task.result
// )}</p>
// <p>创建时间:${parseTime(task.createTime)}</p>`
if (task.endTime) {
html += `<p>结束时间:${parseTime(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>创建时间:${parseTime(activity.startTime)}</p>`
}
if (activity.endTime > 0) {
html += `<p>结束时间:${parseTime(activity.endTime)}</p>`
}
console.log(html)
} else if (element.value.type === 'bpmn:EndEvent' && processInstance.value) {
let optionData = getIntDictOptions(DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT)
let dataResult = ''
optionData.forEach((element) => {
if (element.value == processInstance.value.result) {
dataResult = element.label
}
})
html = `<p>结果:${dataResult}</p>`
// html = `<p>结果:${getIntDictOptions(
// DICT_TYPE.BPM_PROCESS_INSTANCE_RESULT,
// processInstance.value.result
// )}</p>`
if (processInstance.value.endTime) {
html += `<p>结束时间:${parseTime(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
}
const parseTime = (time) => {
if (!time) {
return null
}
const format = '{y}-{m}-{d} {h}:{i}:{s}'
let date
if (typeof time === 'object') {
date = time
} else {
if (typeof time === 'string' && /^[0-9]+$/.test(time)) {
time = parseInt(time)
} else if (typeof time === 'string') {
time = time
.replace(new RegExp(/-/gm), '/')
.replace('T', ' ')
.replace(new RegExp(/\.[\d]{3}/gm), '')
}
if (typeof time === 'number' && time.toString().length === 10) {
time = time * 1000
}
date = new Date(time)
}
const formatObj = {
y: date.getFullYear(),
m: date.getMonth() + 1,
d: date.getDate(),
h: date.getHours(),
i: date.getMinutes(),
s: date.getSeconds(),
a: date.getDay()
}
const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
let value = formatObj[key]
// Note: getDay() returns 0 on Sunday
if (key === 'a') {
return ['日', '一', '二', '三', '四', '五', '六'][value]
}
if (result.length > 0 && value < 10) {
value = '0' + value
}
return value || 0
})
return time_str
}
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>
/** 处理中 */
.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;
}
/** 不通过 */
.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;
}
.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;
}
.element-overlays {
box-sizing: border-box;
padding: 8px;
background: rgba(0, 0, 0, 0.6);
border-radius: 4px;
color: #fafafa;
width: 200px;
}
</style>

View File

@ -0,0 +1,8 @@
import MyProcessDesigner from './ProcessDesigner.vue'
MyProcessDesigner.install = function (Vue) {
Vue.component(MyProcessDesigner.name, MyProcessDesigner)
}
// 流程图的设计器,可编辑
export default MyProcessDesigner

View File

@ -0,0 +1,8 @@
import MyProcessViewer from './ProcessViewer.vue'
MyProcessViewer.install = function (Vue) {
Vue.component(MyProcessViewer.name, MyProcessViewer)
}
// 流程图的查看器,不可编辑
export default MyProcessViewer

View File

@ -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
}

View File

@ -0,0 +1,6 @@
import CustomContextPadProvider from './contentPadProvider'
export default {
__init__: ['contextPadProvider'],
contextPadProvider: ['type', CustomContextPadProvider]
}

View File

@ -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>`
}

View File

@ -0,0 +1,994 @@
{
"name": "Activiti",
"uri": "http://activiti.org/bpmn",
"prefix": "activiti",
"xml": {
"tagAlias": "lowerCase"
},
"associations": [],
"types": [
{
"name": "Definitions",
"isAbstract": true,
"extends": ["bpmn:Definitions"],
"properties": [
{
"name": "diagramRelationId",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "InOutBinding",
"superClass": ["Element"],
"isAbstract": true,
"properties": [
{
"name": "source",
"isAttr": true,
"type": "String"
},
{
"name": "sourceExpression",
"isAttr": true,
"type": "String"
},
{
"name": "target",
"isAttr": true,
"type": "String"
},
{
"name": "businessKey",
"isAttr": true,
"type": "String"
},
{
"name": "local",
"isAttr": true,
"type": "Boolean",
"default": false
},
{
"name": "variables",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "In",
"superClass": ["InOutBinding"],
"meta": {
"allowedIn": ["bpmn:CallActivity"]
}
},
{
"name": "Out",
"superClass": ["InOutBinding"],
"meta": {
"allowedIn": ["bpmn:CallActivity"]
}
},
{
"name": "AsyncCapable",
"isAbstract": true,
"extends": ["bpmn:Activity", "bpmn:Gateway", "bpmn:Event"],
"properties": [
{
"name": "async",
"isAttr": true,
"type": "Boolean",
"default": false
},
{
"name": "asyncBefore",
"isAttr": true,
"type": "Boolean",
"default": false
},
{
"name": "asyncAfter",
"isAttr": true,
"type": "Boolean",
"default": false
},
{
"name": "exclusive",
"isAttr": true,
"type": "Boolean",
"default": true
}
]
},
{
"name": "JobPriorized",
"isAbstract": true,
"extends": ["bpmn:Process", "activiti:AsyncCapable"],
"properties": [
{
"name": "jobPriority",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "SignalEventDefinition",
"isAbstract": true,
"extends": ["bpmn:SignalEventDefinition"],
"properties": [
{
"name": "async",
"isAttr": true,
"type": "Boolean",
"default": false
}
]
},
{
"name": "ErrorEventDefinition",
"isAbstract": true,
"extends": ["bpmn:ErrorEventDefinition"],
"properties": [
{
"name": "errorCodeVariable",
"isAttr": true,
"type": "String"
},
{
"name": "errorMessageVariable",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "Error",
"isAbstract": true,
"extends": ["bpmn:Error"],
"properties": [
{
"name": "activiti:errorMessage",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "PotentialStarter",
"superClass": ["Element"],
"properties": [
{
"name": "resourceAssignmentExpression",
"type": "bpmn:ResourceAssignmentExpression"
}
]
},
{
"name": "FormSupported",
"isAbstract": true,
"extends": ["bpmn:StartEvent", "bpmn:UserTask"],
"properties": [
{
"name": "formHandlerClass",
"isAttr": true,
"type": "String"
},
{
"name": "formKey",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "TemplateSupported",
"isAbstract": true,
"extends": ["bpmn:Process", "bpmn:FlowElement"],
"properties": [
{
"name": "modelerTemplate",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "Initiator",
"isAbstract": true,
"extends": ["bpmn:StartEvent"],
"properties": [
{
"name": "initiator",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "ScriptTask",
"isAbstract": true,
"extends": ["bpmn:ScriptTask"],
"properties": [
{
"name": "resultVariable",
"isAttr": true,
"type": "String"
},
{
"name": "resource",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "Process",
"isAbstract": true,
"extends": ["bpmn:Process"],
"properties": [
{
"name": "candidateStarterGroups",
"isAttr": true,
"type": "String"
},
{
"name": "candidateStarterUsers",
"isAttr": true,
"type": "String"
},
{
"name": "versionTag",
"isAttr": true,
"type": "String"
},
{
"name": "historyTimeToLive",
"isAttr": true,
"type": "String"
},
{
"name": "isStartableInTasklist",
"isAttr": true,
"type": "Boolean",
"default": true
},
{
"name": "executionListener",
"isAbstract": true,
"type": "Expression"
}
]
},
{
"name": "EscalationEventDefinition",
"isAbstract": true,
"extends": ["bpmn:EscalationEventDefinition"],
"properties": [
{
"name": "escalationCodeVariable",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "FormalExpression",
"isAbstract": true,
"extends": ["bpmn:FormalExpression"],
"properties": [
{
"name": "resource",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "multiinstance_type",
"superClass": ["Element"]
},
{
"name": "multiinstance_condition",
"superClass": ["Element"]
},
{
"name": "Assignable",
"extends": ["bpmn:UserTask"],
"properties": [
{
"name": "assignee",
"isAttr": true,
"type": "String"
},
{
"name": "candidateUsers",
"isAttr": true,
"type": "String"
},
{
"name": "candidateGroups",
"isAttr": true,
"type": "String"
},
{
"name": "dueDate",
"isAttr": true,
"type": "String"
},
{
"name": "followUpDate",
"isAttr": true,
"type": "String"
},
{
"name": "priority",
"isAttr": true,
"type": "String"
},
{
"name": "multiinstance_condition",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "CallActivity",
"extends": ["bpmn:CallActivity"],
"properties": [
{
"name": "calledElementBinding",
"isAttr": true,
"type": "String",
"default": "latest"
},
{
"name": "calledElementVersion",
"isAttr": true,
"type": "String"
},
{
"name": "calledElementVersionTag",
"isAttr": true,
"type": "String"
},
{
"name": "calledElementTenantId",
"isAttr": true,
"type": "String"
},
{
"name": "caseRef",
"isAttr": true,
"type": "String"
},
{
"name": "caseBinding",
"isAttr": true,
"type": "String",
"default": "latest"
},
{
"name": "caseVersion",
"isAttr": true,
"type": "String"
},
{
"name": "caseTenantId",
"isAttr": true,
"type": "String"
},
{
"name": "variableMappingClass",
"isAttr": true,
"type": "String"
},
{
"name": "variableMappingDelegateExpression",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "ServiceTaskLike",
"extends": [
"bpmn:ServiceTask",
"bpmn:BusinessRuleTask",
"bpmn:SendTask",
"bpmn:MessageEventDefinition"
],
"properties": [
{
"name": "expression",
"isAttr": true,
"type": "String"
},
{
"name": "class",
"isAttr": true,
"type": "String"
},
{
"name": "delegateExpression",
"isAttr": true,
"type": "String"
},
{
"name": "resultVariable",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "DmnCapable",
"extends": ["bpmn:BusinessRuleTask"],
"properties": [
{
"name": "decisionRef",
"isAttr": true,
"type": "String"
},
{
"name": "decisionRefBinding",
"isAttr": true,
"type": "String",
"default": "latest"
},
{
"name": "decisionRefVersion",
"isAttr": true,
"type": "String"
},
{
"name": "mapDecisionResult",
"isAttr": true,
"type": "String",
"default": "resultList"
},
{
"name": "decisionRefTenantId",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "ExternalCapable",
"extends": ["activiti:ServiceTaskLike"],
"properties": [
{
"name": "type",
"isAttr": true,
"type": "String"
},
{
"name": "topic",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "TaskPriorized",
"extends": ["bpmn:Process", "activiti:ExternalCapable"],
"properties": [
{
"name": "taskPriority",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "Properties",
"superClass": ["Element"],
"meta": {
"allowedIn": ["*"]
},
"properties": [
{
"name": "values",
"type": "Property",
"isMany": true
}
]
},
{
"name": "Property",
"superClass": ["Element"],
"properties": [
{
"name": "id",
"type": "String",
"isAttr": true
},
{
"name": "name",
"type": "String",
"isAttr": true
},
{
"name": "value",
"type": "String",
"isAttr": true
}
]
},
{
"name": "Connector",
"superClass": ["Element"],
"meta": {
"allowedIn": ["activiti:ServiceTaskLike"]
},
"properties": [
{
"name": "inputOutput",
"type": "InputOutput"
},
{
"name": "connectorId",
"type": "String"
}
]
},
{
"name": "InputOutput",
"superClass": ["Element"],
"meta": {
"allowedIn": ["bpmn:FlowNode", "activiti:Connector"]
},
"properties": [
{
"name": "inputOutput",
"type": "InputOutput"
},
{
"name": "connectorId",
"type": "String"
},
{
"name": "inputParameters",
"isMany": true,
"type": "InputParameter"
},
{
"name": "outputParameters",
"isMany": true,
"type": "OutputParameter"
}
]
},
{
"name": "InputOutputParameter",
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "value",
"isBody": true,
"type": "String"
},
{
"name": "definition",
"type": "InputOutputParameterDefinition"
}
]
},
{
"name": "InputOutputParameterDefinition",
"isAbstract": true
},
{
"name": "List",
"superClass": ["InputOutputParameterDefinition"],
"properties": [
{
"name": "items",
"isMany": true,
"type": "InputOutputParameterDefinition"
}
]
},
{
"name": "Map",
"superClass": ["InputOutputParameterDefinition"],
"properties": [
{
"name": "entries",
"isMany": true,
"type": "Entry"
}
]
},
{
"name": "Entry",
"properties": [
{
"name": "key",
"isAttr": true,
"type": "String"
},
{
"name": "value",
"isBody": true,
"type": "String"
},
{
"name": "definition",
"type": "InputOutputParameterDefinition"
}
]
},
{
"name": "Value",
"superClass": ["InputOutputParameterDefinition"],
"properties": [
{
"name": "id",
"isAttr": true,
"type": "String"
},
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "value",
"isBody": true,
"type": "String"
}
]
},
{
"name": "Script",
"superClass": ["InputOutputParameterDefinition"],
"properties": [
{
"name": "scriptFormat",
"isAttr": true,
"type": "String"
},
{
"name": "resource",
"isAttr": true,
"type": "String"
},
{
"name": "value",
"isBody": true,
"type": "String"
}
]
},
{
"name": "Field",
"superClass": ["Element"],
"meta": {
"allowedIn": [
"activiti:ServiceTaskLike",
"activiti:ExecutionListener",
"activiti:TaskListener"
]
},
"properties": [
{
"name": "name",
"isAttr": true,
"type": "String"
},
{
"name": "expression",
"type": "String"
},
{
"name": "stringValue",
"isAttr": true,
"type": "String"
},
{
"name": "string",
"type": "String"
}
]
},
{
"name": "InputParameter",
"superClass": ["InputOutputParameter"]
},
{
"name": "OutputParameter",
"superClass": ["InputOutputParameter"]
},
{
"name": "Collectable",
"isAbstract": true,
"extends": ["bpmn:MultiInstanceLoopCharacteristics"],
"superClass": ["activiti:AsyncCapable"],
"properties": [
{
"name": "collection",
"isAttr": true,
"type": "String"
},
{
"name": "elementVariable",
"isAttr": true,
"type": "String"
}
]
},
{
"name": "FailedJobRetryTimeCycle",
"superClass": ["Element"],
"meta": {
"allowedIn": ["activiti:AsyncCapable", "bpmn:MultiInstanceLoopCharacteristics"]
},
"properties": [
{
"name": "body",
"isBody": true,
"type": "String"
}
]
},
{
"name": "ExecutionListener",
"superClass": ["Element"],
"meta": {
"allowedIn": [
"bpmn:Task",
"bpmn:ServiceTask",
"bpmn:UserTask",
"bpmn:BusinessRuleTask",
"bpmn:ScriptTask",
"bpmn:ReceiveTask",
"bpmn:ManualTask",
"bpmn:ExclusiveGateway",
"bpmn:SequenceFlow",
"bpmn:ParallelGateway",
"bpmn:InclusiveGateway",
"bpmn:EventBasedGateway",
"bpmn:StartEvent",
"bpmn:IntermediateCatchEvent",
"bpmn:IntermediateThrowEvent",
"bpmn:EndEvent",
"bpmn:BoundaryEvent",
"bpmn:CallActivity",
"bpmn:SubProcess",
"bpmn:Process"
]
},
"properties": [
{
"name": "expression",
"isAttr": true,
"type": "String"
},
{
"name": "class",
"isAttr": true,
"type": "String"
},
{
"name": "delegateExpression",
"isAttr": true,
"type": "String"
},
{
"name": "event",
"isAttr": true,
"type": "String"
},
{
"name": "script",
"type": "Script"
},
{
"name": "fields",
"type": "Field",
"isMany": true
}
]
},
{
"name": "TaskListener",
"superClass": ["Element"],
"meta": {
"allowedIn": ["bpmn:UserTask"]
},
"properties": [
{
"name": "expression",
"isAttr": true,
"type": "String"
},
{
"name": "class",
"isAttr": true,
"type": "String"
},
{
"name": "delegateExpression",
"isAttr": true,
"type": "String"
},
{
"name": "event",
"isAttr": true,
"type": "String"
},
{
"name": "script",
"type": "Script"
},
{
"name": "fields",
"type": "Field",
"isMany": true
}
]
},
{
"name": "FormProperty",
"superClass": ["Element"],
"meta": {
"allowedIn": ["bpmn:StartEvent", "bpmn:UserTask"]
},
"properties": [
{
"name": "id",
"type": "String",
"isAttr": true
},
{
"name": "name",
"type": "String",
"isAttr": true
},
{
"name": "type",
"type": "String",
"isAttr": true
},
{
"name": "required",
"type": "String",
"isAttr": true
},
{
"name": "readable",
"type": "String",
"isAttr": true
},
{
"name": "writable",
"type": "String",
"isAttr": true
},
{
"name": "variable",
"type": "String",
"isAttr": true
},
{
"name": "expression",
"type": "String",
"isAttr": true
},
{
"name": "datePattern",
"type": "String",
"isAttr": true
},
{
"name": "default",
"type": "String",
"isAttr": true
},
{
"name": "values",
"type": "Value",
"isMany": true
}
]
},
{
"name": "FormProperty",
"superClass": ["Element"],
"properties": [
{
"name": "id",
"type": "String",
"isAttr": true
},
{
"name": "label",
"type": "String",
"isAttr": true
},
{
"name": "type",
"type": "String",
"isAttr": true
},
{
"name": "datePattern",
"type": "String",
"isAttr": true
},
{
"name": "defaultValue",
"type": "String",
"isAttr": true
},
{
"name": "properties",
"type": "Properties"
},
{
"name": "validation",
"type": "Validation"
},
{
"name": "values",
"type": "Value",
"isMany": true
}
]
},
{
"name": "Validation",
"superClass": ["Element"],
"properties": [
{
"name": "constraints",
"type": "Constraint",
"isMany": true
}
]
},
{
"name": "Constraint",
"superClass": ["Element"],
"properties": [
{
"name": "name",
"type": "String",
"isAttr": true
},
{
"name": "config",
"type": "String",
"isAttr": true
}
]
},
{
"name": "ConditionalEventDefinition",
"isAbstract": true,
"extends": ["bpmn:ConditionalEventDefinition"],
"properties": [
{
"name": "variableName",
"isAttr": true,
"type": "String"
},
{
"name": "variableEvent",
"isAttr": true,
"type": "String"
}
]
}
],
"emumerations": []
}

View File

@ -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

View File

@ -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]
}

View File

@ -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
}

View File

@ -0,0 +1,8 @@
'use strict'
import extension from './extension'
export default {
__init__: ['camundaModdleExtension'],
camundaModdleExtension: ['type', extension]
}

View File

@ -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

View File

@ -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]
}

View File

@ -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的构造器指向防止原型链的混乱

View File

@ -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]
}

View File

@ -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
}

View File

@ -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 + '}'
})
}
}

View File

@ -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.': '指定多个组作为逗号分隔的列表。'
}