workflow-bpmn-modeler-andtv-vue3 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/LICENSE +21 -0
  3. package/dist/demo.html +9 -0
  4. package/dist/fonts/bpmn.5d33bee4.eot +0 -0
  5. package/dist/fonts/bpmn.67058807.woff2 +0 -0
  6. package/dist/fonts/bpmn.b5c9250d.ttf +0 -0
  7. package/dist/fonts/bpmn.e9e7d076.woff +0 -0
  8. package/dist/img/bpmn.74eea12b.svg +224 -0
  9. package/dist/workflow-bpmn-modeler-andtv-vue3.common.js +5617 -0
  10. package/dist/workflow-bpmn-modeler-andtv-vue3.umd.js +5628 -0
  11. package/dist/workflow-bpmn-modeler-andtv-vue3.umd.min.js +5628 -0
  12. package/package/BpmData.js +68 -0
  13. package/package/PropertyPanel.vue +342 -0
  14. package/package/common/customTranslate.js +20 -0
  15. package/package/common/mixinExecutionListener.js +24 -0
  16. package/package/common/mixinPanel.js +70 -0
  17. package/package/common/mixinXcrud.js +22 -0
  18. package/package/common/parseElement.js +53 -0
  19. package/package/components/custom/customContextPad.vue +24 -0
  20. package/package/components/nodePanel/gateway.vue +165 -0
  21. package/package/components/nodePanel/process.vue +201 -0
  22. package/package/components/nodePanel/property/executionListener.vue +240 -0
  23. package/package/components/nodePanel/property/listenerParam.vue +137 -0
  24. package/package/components/nodePanel/property/multiInstance.vue +177 -0
  25. package/package/components/nodePanel/property/signal.vue +178 -0
  26. package/package/components/nodePanel/property/taskListener.vue +242 -0
  27. package/package/components/nodePanel/sequenceFlow.vue +180 -0
  28. package/package/components/nodePanel/startEnd.vue +174 -0
  29. package/package/components/nodePanel/task.vue +452 -0
  30. package/package/flowable/flowable.json +1218 -0
  31. package/package/flowable/init.js +24 -0
  32. package/package/flowable/showConfig.js +51 -0
  33. package/package/index.js +8 -0
  34. package/package/index.ts +9 -0
  35. package/package/index.vue +730 -0
  36. package/package/lang/zh.js +227 -0
  37. package/package/types/components.d.ts +35 -0
  38. package/package.json +73 -0
@@ -0,0 +1,242 @@
1
+ <template>
2
+ <div>
3
+ <a-modal
4
+ v-model:open="dialogVisible"
5
+ title="任务监听器"
6
+ width="900px"
7
+ :mask-closable="false"
8
+ :keyboard="false"
9
+ :closable="false"
10
+ @cancel="$emit('close')"
11
+ >
12
+ <a-form ref="formRef" :model="formData" layout="vertical">
13
+ <a-tabs>
14
+ <a-tab-pane key="taskListener" tab="任务监听器">
15
+ <div style="margin-bottom: 16px;">
16
+ <a-button type="primary" size="small" @click="addTaskListener">
17
+ <template #icon><PlusOutlined /></template>
18
+ 添加任务监听器
19
+ </a-button>
20
+ </div>
21
+
22
+ <a-table
23
+ :columns="columns"
24
+ :data-source="formData.taskListener"
25
+ :pagination="false"
26
+ size="small"
27
+ >
28
+ <template #bodyCell="{ column, record, index }">
29
+ <template v-if="column.key === 'event'">
30
+ <a-select v-model:value="record.event" size="small" style="width: 100%">
31
+ <a-select-option value="create">create</a-select-option>
32
+ <a-select-option value="assignment">assignment</a-select-option>
33
+ <a-select-option value="complete">complete</a-select-option>
34
+ <a-select-option value="delete">delete</a-select-option>
35
+ </a-select>
36
+ </template>
37
+ <template v-if="column.key === 'type'">
38
+ <a-select v-model:value="record.type" size="small" style="width: 100%">
39
+ <a-select-option value="class">class</a-select-option>
40
+ <a-select-option value="expression">expression</a-select-option>
41
+ <a-select-option value="delegateExpression">delegateExpression</a-select-option>
42
+ </a-select>
43
+ </template>
44
+ <template v-if="column.key === 'className'">
45
+ <a-input v-model:value="record.className" size="small" placeholder="请输入类名" />
46
+ </template>
47
+ <template v-if="column.key === 'params'">
48
+ <a-badge :count="record.params ? record.params.length : 0">
49
+ <a-button size="small" @click="configParam(index)">配置</a-button>
50
+ </a-badge>
51
+ </template>
52
+ <template v-if="column.key === 'action'">
53
+ <a-button type="link" danger size="small" @click="removeTaskListener(index)">删除</a-button>
54
+ </template>
55
+ </template>
56
+ </a-table>
57
+ </a-tab-pane>
58
+ </a-tabs>
59
+ </a-form>
60
+
61
+ <template #footer>
62
+ <a-button type="primary" @click="closeDialog">确 定</a-button>
63
+ </template>
64
+ </a-modal>
65
+
66
+ <listenerParam v-if="showParamDialog && nowIndex !== null" :value="formData.taskListener[nowIndex]?.params" @close="finishConfigParam" />
67
+ </div>
68
+ </template>
69
+
70
+ <script setup lang="ts">
71
+ import { ref, onMounted, nextTick } from 'vue'
72
+ import { FormInstance } from 'ant-design-vue'
73
+ import { PlusOutlined } from '@ant-design/icons-vue'
74
+ import listenerParam from './listenerParam.vue'
75
+ import Modeler from 'bpmn-js/lib/Modeler'
76
+
77
+ defineOptions({
78
+ name: 'TaskListenerDialog'
79
+ })
80
+
81
+ interface Props {
82
+ element: any
83
+ modeler: Modeler
84
+ }
85
+
86
+ const props = defineProps<Props>()
87
+ const emit = defineEmits<{
88
+ close: []
89
+ }>()
90
+
91
+ const formRef = ref<FormInstance>()
92
+ const dialogVisible = ref(true)
93
+ const showParamDialog = ref(false)
94
+ const nowIndex = ref<number | null>(null)
95
+ const formData = ref({
96
+ taskListener: [] as any[]
97
+ })
98
+
99
+ const columns = [
100
+ {
101
+ title: '事件',
102
+ dataIndex: 'event',
103
+ key: 'event',
104
+ width: 120
105
+ },
106
+ {
107
+ title: '类型',
108
+ dataIndex: 'type',
109
+ key: 'type',
110
+ width: 150
111
+ },
112
+ {
113
+ title: 'Java类名',
114
+ dataIndex: 'className',
115
+ key: 'className'
116
+ },
117
+ {
118
+ title: '参数',
119
+ key: 'params',
120
+ width: 100
121
+ },
122
+ {
123
+ title: '操作',
124
+ key: 'action',
125
+ width: 80
126
+ }
127
+ ]
128
+
129
+ const updateProperties = (properties: Record<string, any>) => {
130
+ nextTick(() => {
131
+ try {
132
+ const modeling = props.modeler.get('modeling')
133
+ modeling.updateProperties(props.element, properties)
134
+ } catch (error) {
135
+ console.warn('Update properties error:', error)
136
+ }
137
+ })
138
+ }
139
+
140
+ const addTaskListener = () => {
141
+ formData.value.taskListener.push({
142
+ event: 'create',
143
+ type: 'class',
144
+ className: '',
145
+ params: []
146
+ })
147
+ }
148
+
149
+ const removeTaskListener = (index: number) => {
150
+ formData.value.taskListener.splice(index, 1)
151
+ }
152
+
153
+ const configParam = (index: number) => {
154
+ nowIndex.value = index
155
+ const nowObj = formData.value.taskListener[index]
156
+ if (!nowObj.params) {
157
+ nowObj.params = []
158
+ }
159
+ showParamDialog.value = true
160
+ }
161
+
162
+ const finishConfigParam = (param: any) => {
163
+ showParamDialog.value = false
164
+ if (nowIndex.value !== null) {
165
+ const cache = formData.value.taskListener[nowIndex.value]
166
+ cache.params = param
167
+ formData.value.taskListener[nowIndex.value] = { ...cache }
168
+ }
169
+ nowIndex.value = null
170
+ }
171
+
172
+ const updateElement = () => {
173
+ if (formData.value.taskListener?.length) {
174
+ let extensionElements = props.element.businessObject.get('extensionElements')
175
+ if (!extensionElements) {
176
+ extensionElements = props.modeler.get('moddle').create('bpmn:ExtensionElements')
177
+ }
178
+ // 清除旧值
179
+ extensionElements.values = extensionElements.values?.filter((item: any) => item.$type !== 'flowable:TaskListener') ?? []
180
+ formData.value.taskListener.forEach((item: any) => {
181
+ const taskListener = props.modeler.get('moddle').create('flowable:TaskListener')
182
+ taskListener['event'] = item.event
183
+ taskListener[item.type] = item.className
184
+ if (item.params && item.params.length) {
185
+ item.params.forEach((field: any) => {
186
+ const fieldElement = props.modeler.get('moddle').create('flowable:Field')
187
+ fieldElement['name'] = field.name
188
+ fieldElement[field.type] = field.value
189
+ taskListener.get('fields').push(fieldElement)
190
+ })
191
+ }
192
+ extensionElements.get('values').push(taskListener)
193
+ })
194
+ updateProperties({ extensionElements: extensionElements })
195
+ } else {
196
+ const extensionElements = props.element.businessObject[`extensionElements`]
197
+ if (extensionElements) {
198
+ extensionElements.values = extensionElements.values?.filter((item: any) => item.$type !== 'flowable:TaskListener') ?? []
199
+ }
200
+ }
201
+ }
202
+
203
+ const closeDialog = () => {
204
+ updateElement()
205
+ dialogVisible.value = false
206
+ emit('close')
207
+ }
208
+
209
+ onMounted(() => {
210
+ if (props.element?.businessObject?.extensionElements?.values) {
211
+ formData.value.taskListener = props.element.businessObject.extensionElements.values
212
+ .filter((item: any) => item.$type === 'flowable:TaskListener')
213
+ .map((item: any) => {
214
+ let type
215
+ if ('class' in item) type = 'class'
216
+ if ('expression' in item) type = 'expression'
217
+ if ('delegateExpression' in item) type = 'delegateExpression'
218
+ return {
219
+ event: item.event,
220
+ type: type,
221
+ className: type ? item[type] : '',
222
+ params: item.fields?.map((field: any) => {
223
+ let fieldType
224
+ if ('stringValue' in field) fieldType = 'stringValue'
225
+ if ('expression' in field) fieldType = 'expression'
226
+ return {
227
+ name: field.name,
228
+ type: fieldType,
229
+ value: fieldType ? field[fieldType] : ''
230
+ }
231
+ }) ?? []
232
+ }
233
+ })
234
+ }
235
+ })
236
+ </script>
237
+
238
+ <style scoped>
239
+ .flow-containers .ant-badge {
240
+ margin-right: 8px;
241
+ }
242
+ </style>
@@ -0,0 +1,180 @@
1
+ <template>
2
+ <div>
3
+ <a-form ref="formRef" :model="formData" layout="horizontal">
4
+ <a-form-item label="节点 id" name="id" :rules="[{ required: true, message: 'Id 不能为空' }]">
5
+ <a-input v-model:value="formData.id" />
6
+ </a-form-item>
7
+
8
+ <a-form-item label="节点名称" name="name">
9
+ <a-input v-model:value="formData.name" />
10
+ </a-form-item>
11
+
12
+ <a-form-item label="节点描述" name="documentation">
13
+ <a-textarea v-model:value="formData.documentation" />
14
+ </a-form-item>
15
+
16
+ <a-form-item label="执行监听器">
17
+ <a-badge :count="executionListenerLength">
18
+ <a-button size="small" @click="dialogName = 'executionListenerDialog'">编辑</a-button>
19
+ </a-badge>
20
+ </a-form-item>
21
+
22
+ <a-form-item label="跳转条件" name="conditionExpression">
23
+ <a-input v-model:value="formData.conditionExpression" />
24
+ </a-form-item>
25
+
26
+ <a-form-item label="跳过表达式" name="skipExpression">
27
+ <a-input v-model:value="formData.skipExpression" />
28
+ </a-form-item>
29
+ </a-form>
30
+
31
+ <executionListenerDialog
32
+ v-if="dialogName === 'executionListenerDialog'"
33
+ :element="element"
34
+ :modeler="modeler"
35
+ @close="finishExecutionListener"
36
+ />
37
+ </div>
38
+ </template>
39
+
40
+ <script setup lang="ts">
41
+ import { ref, computed, watch, onMounted, nextTick } from 'vue'
42
+ import { FormInstance } from 'ant-design-vue'
43
+ import executionListenerDialog from './property/executionListener.vue'
44
+ import { commonParse, conditionExpressionParse } from '../../common/parseElement'
45
+ import showConfigData from '../../flowable/showConfig'
46
+ import Modeler from 'bpmn-js/lib/Modeler'
47
+
48
+ defineOptions({
49
+ name: 'SequenceFlowPanel'
50
+ })
51
+
52
+ interface Props {
53
+ modeler: Modeler
54
+ element: any
55
+ }
56
+
57
+ const props = defineProps<Props>()
58
+
59
+ const formRef = ref<FormInstance>()
60
+ const dialogName = ref('')
61
+ const executionListenerLength = ref(0)
62
+ const formData = ref<Record<string, any>>({})
63
+
64
+ const elementType = computed(() => {
65
+ const bizObj = props.element.businessObject
66
+ return bizObj.eventDefinitions
67
+ ? bizObj.eventDefinitions[0].$type
68
+ : bizObj.$type
69
+ })
70
+
71
+ const showConfig = computed(() => (showConfigData as any)[elementType.value] || {})
72
+
73
+ const updateProperties = (properties: Record<string, any>) => {
74
+ nextTick(() => {
75
+ try {
76
+ const modeling = props.modeler.get('modeling')
77
+ modeling.updateProperties(props.element, properties)
78
+ } catch (error) {
79
+ console.warn('Update properties error:', error)
80
+ }
81
+ })
82
+ }
83
+
84
+ const computedExecutionListenerLength = () => {
85
+ if (props.element?.businessObject?.extensionElements?.values) {
86
+ executionListenerLength.value = props.element.businessObject.extensionElements.values
87
+ .filter((item: any) => item.$type === 'flowable:ExecutionListener').length
88
+ } else {
89
+ executionListenerLength.value = 0
90
+ }
91
+ }
92
+
93
+ const finishExecutionListener = () => {
94
+ if (dialogName.value === 'executionListenerDialog') {
95
+ computedExecutionListenerLength()
96
+ }
97
+ dialogName.value = ''
98
+ }
99
+
100
+ // Watch for form data changes
101
+ watch(() => formData.value.conditionExpression, (val) => {
102
+ if (val && props.modeler?.get('moddle')) {
103
+ const newCondition = props.modeler.get('moddle').create('bpmn:FormalExpression', { body: val })
104
+ updateProperties({ conditionExpression: newCondition })
105
+ } else {
106
+ updateProperties({ conditionExpression: null })
107
+ }
108
+ })
109
+
110
+ watch(() => formData.value.skipExpression, (val) => {
111
+ if (val === '') val = null
112
+ updateProperties({ 'flowable:skipExpression': val })
113
+ })
114
+
115
+ watch(() => formData.value.id, (val) => {
116
+ updateProperties({ id: val })
117
+ })
118
+
119
+ watch(() => formData.value.name, (val) => {
120
+ updateProperties({ name: val })
121
+ })
122
+
123
+ watch(() => formData.value.documentation, (val) => {
124
+ if (!val) {
125
+ updateProperties({ documentation: [] })
126
+ return
127
+ }
128
+ if (props.modeler?.get('moddle')) {
129
+ const documentationElement = props.modeler.get('moddle').create('bpmn:Documentation', { text: val })
130
+ updateProperties({ documentation: [documentationElement] })
131
+ }
132
+ })
133
+
134
+ onMounted(() => {
135
+ if (props.element) {
136
+ let cache = commonParse(props.element)
137
+ cache = conditionExpressionParse(cache)
138
+ formData.value = cache
139
+ computedExecutionListenerLength()
140
+ }
141
+ })
142
+ </script>
143
+
144
+ <style scoped>
145
+ .flow-containers {
146
+ padding: 0;
147
+ background: transparent;
148
+ min-height: 100%;
149
+ }
150
+
151
+ .flow-containers :deep(.ant-form-item) {
152
+ margin-bottom: 16px;
153
+ }
154
+
155
+ .flow-containers :deep(.ant-form-item-label) {
156
+ font-weight: 500;
157
+ color: #262626;
158
+ }
159
+
160
+ .flow-containers :deep(.ant-input),
161
+ .flow-containers :deep(.ant-select),
162
+ .flow-containers :deep(.ant-textarea) {
163
+ border-radius: 6px;
164
+ transition: all 0.3s;
165
+ }
166
+
167
+ .flow-containers :deep(.ant-button) {
168
+ border-radius: 6px;
169
+ font-weight: 500;
170
+ }
171
+
172
+ .flow-containers :deep(.ant-badge) {
173
+ margin-right: 8px;
174
+ }
175
+
176
+ .flow-containers :deep(.ant-badge .ant-badge-count) {
177
+ background: linear-gradient(135deg, #ff4d4f 0%, #ff7875 100%);
178
+ box-shadow: 0 2px 4px rgba(255, 77, 79, 0.3);
179
+ }
180
+ </style>
@@ -0,0 +1,174 @@
1
+ <template>
2
+ <div>
3
+ <a-form ref="formRef" :model="formData" layout="horizontal">
4
+ <a-form-item label="节点 id" name="id" :rules="[{ required: true, message: 'Id 不能为空' }]">
5
+ <a-input v-model:value="formData.id" />
6
+ </a-form-item>
7
+
8
+ <a-form-item label="节点名称" name="name">
9
+ <a-input v-model:value="formData.name" />
10
+ </a-form-item>
11
+
12
+ <a-form-item label="节点描述" name="documentation">
13
+ <a-textarea v-model:value="formData.documentation" />
14
+ </a-form-item>
15
+
16
+ <a-form-item label="执行监听器">
17
+ <a-badge :count="executionListenerLength">
18
+ <a-button size="small" @click="dialogName = 'executionListenerDialog'">编辑</a-button>
19
+ </a-badge>
20
+ </a-form-item>
21
+
22
+ <a-form-item v-if="showConfig.initiator" label="发起人" name="initiator">
23
+ <a-input v-model:value="formData.initiator" />
24
+ </a-form-item>
25
+
26
+ <a-form-item v-if="showConfig.formKey" label="表单标识key" name="formKey">
27
+ <a-input v-model:value="formData.formKey" />
28
+ </a-form-item>
29
+ </a-form>
30
+
31
+ <executionListenerDialog
32
+ v-if="dialogName === 'executionListenerDialog'"
33
+ :element="element"
34
+ :modeler="modeler"
35
+ @close="finishExecutionListener"
36
+ />
37
+ </div>
38
+ </template>
39
+
40
+ <script setup lang="ts">
41
+ import { ref, computed, watch, onMounted, nextTick } from 'vue'
42
+ import { FormInstance } from 'ant-design-vue'
43
+ import executionListenerDialog from './property/executionListener.vue'
44
+ import { commonParse } from '../../common/parseElement'
45
+ import showConfigData from '../../flowable/showConfig'
46
+ import Modeler from 'bpmn-js/lib/Modeler'
47
+
48
+ defineOptions({
49
+ name: 'StartEndPanel'
50
+ })
51
+
52
+ interface Props {
53
+ modeler: Modeler
54
+ element: any
55
+ }
56
+
57
+ const props = defineProps<Props>()
58
+
59
+ const formRef = ref<FormInstance>()
60
+ const dialogName = ref('')
61
+ const executionListenerLength = ref(0)
62
+ const formData = ref<Record<string, any>>({})
63
+
64
+ const elementType = computed(() => {
65
+ const bizObj = props.element.businessObject
66
+ return bizObj.eventDefinitions
67
+ ? bizObj.eventDefinitions[0].$type
68
+ : bizObj.$type
69
+ })
70
+
71
+ const showConfig = computed(() => (showConfigData as any)[elementType.value] || {})
72
+
73
+ const updateProperties = (properties: Record<string, any>) => {
74
+ nextTick(() => {
75
+ try {
76
+ const modeling = props.modeler.get('modeling')
77
+ modeling.updateProperties(props.element, properties)
78
+ } catch (error) {
79
+ console.warn('Update properties error:', error)
80
+ }
81
+ })
82
+ }
83
+
84
+ const computedExecutionListenerLength = () => {
85
+ if (props.element?.businessObject?.extensionElements?.values) {
86
+ executionListenerLength.value = props.element.businessObject.extensionElements.values
87
+ .filter((item: any) => item.$type === 'flowable:ExecutionListener').length
88
+ } else {
89
+ executionListenerLength.value = 0
90
+ }
91
+ }
92
+
93
+ const finishExecutionListener = () => {
94
+ if (dialogName.value === 'executionListenerDialog') {
95
+ computedExecutionListenerLength()
96
+ }
97
+ dialogName.value = ''
98
+ }
99
+
100
+ // Watch for form data changes
101
+ watch(() => formData.value.initiator, (val) => {
102
+ if (val === '') val = null
103
+ updateProperties({ 'flowable:initiator': val })
104
+ })
105
+
106
+ watch(() => formData.value.formKey, (val) => {
107
+ if (val === '') val = null
108
+ updateProperties({ 'flowable:formKey': val })
109
+ })
110
+
111
+ watch(() => formData.value.id, (val) => {
112
+ updateProperties({ id: val })
113
+ })
114
+
115
+ watch(() => formData.value.name, (val) => {
116
+ updateProperties({ name: val })
117
+ })
118
+
119
+ watch(() => formData.value.documentation, (val) => {
120
+ if (!val) {
121
+ updateProperties({ documentation: [] })
122
+ return
123
+ }
124
+ if (props.modeler?.get('moddle')) {
125
+ const documentationElement = props.modeler.get('moddle').create('bpmn:Documentation', { text: val })
126
+ updateProperties({ documentation: [documentationElement] })
127
+ }
128
+ })
129
+
130
+ onMounted(() => {
131
+ if (props.element) {
132
+ formData.value = commonParse(props.element)
133
+ computedExecutionListenerLength()
134
+ }
135
+ })
136
+ </script>
137
+
138
+ <style scoped>
139
+ .flow-containers {
140
+ padding: 0;
141
+ background: transparent;
142
+ min-height: 100%;
143
+ }
144
+
145
+ .flow-containers :deep(.ant-form-item) {
146
+ margin-bottom: 16px;
147
+ }
148
+
149
+ .flow-containers :deep(.ant-form-item-label) {
150
+ font-weight: 500;
151
+ color: #262626;
152
+ }
153
+
154
+ .flow-containers :deep(.ant-input),
155
+ .flow-containers :deep(.ant-select),
156
+ .flow-containers :deep(.ant-textarea) {
157
+ border-radius: 6px;
158
+ transition: all 0.3s;
159
+ }
160
+
161
+ .flow-containers :deep(.ant-button) {
162
+ border-radius: 6px;
163
+ font-weight: 500;
164
+ }
165
+
166
+ .flow-containers :deep(.ant-badge) {
167
+ margin-right: 8px;
168
+ }
169
+
170
+ .flow-containers :deep(.ant-badge .ant-badge-count) {
171
+ background: linear-gradient(135deg, #ff4d4f 0%, #ff7875 100%);
172
+ box-shadow: 0 2px 4px rgba(255, 77, 79, 0.3);
173
+ }
174
+ </style>