潘志宝
3 天以前 221918bba28d2384d03c596a68256d7832e4a0e0
提交 | 用户 | 时间
820397 1 <template>
H 2   <ContentWrap>
3     <!-- 流程设计器,负责绘制流程等 -->
4     <MyProcessDesigner
5       key="designer"
6       v-model="xmlString"
7       :value="xmlString"
8       v-bind="controlForm"
9       keyboard
10       ref="processDesigner"
11       @init-finished="initModeler"
12       :additionalModel="controlForm.additionalModel"
c9a6f7 13       :model="model"
820397 14       @save="save"
H 15     />
16     <!-- 流程属性器,负责编辑每个流程节点的属性 -->
17     <MyProcessPenal
c9a6f7 18       v-if="isModelerReady && modeler"
820397 19       key="penal"
c9a6f7 20       :bpmnModeler="modeler"
820397 21       :prefix="controlForm.prefix"
H 22       class="process-panel"
23       :model="model"
24     />
25   </ContentWrap>
26 </template>
27
28 <script lang="ts" setup>
29 import { MyProcessDesigner, MyProcessPenal } from '@/components/bpmnProcessDesigner/package'
30 // 自定义元素选中时的弹出菜单(修改 默认任务 为 用户任务)
31 import CustomContentPadProvider from '@/components/bpmnProcessDesigner/package/designer/plugins/content-pad'
32 // 自定义左侧菜单(修改 默认任务 为 用户任务)
33 import CustomPaletteProvider from '@/components/bpmnProcessDesigner/package/designer/plugins/palette'
34 import * as ModelApi from '@/api/bpm/model'
35
36 defineOptions({ name: 'BpmModelEditor' })
37
c9a6f7 38 const props = defineProps<{
H 39   modelId?: string
40   modelKey?: string
41   modelName?: string
42   value?: string
43 }>()
44
45 const emit = defineEmits(['success', 'init-finished'])
820397 46 const message = useMessage() // 国际化
9259c2 47
H 48 // 表单信息
49 const formFields = ref<string[]>([])
50 const formType = ref(20)
51 provide('formFields', formFields)
52 provide('formType', formType)
820397 53
c9a6f7 54 const xmlString = ref<string>('') // BPMN XML
H 55 const modeler = shallowRef() // BPMN Modeler
56 const processDesigner = ref()
57 const isModelerReady = ref(false)
820397 58 const controlForm = ref({
H 59   simulation: true,
60   labelEditing: false,
61   labelVisible: false,
62   prefix: 'flowable',
63   headerButtonSize: 'mini',
64   additionalModel: [CustomContentPadProvider, CustomPaletteProvider]
65 })
66 const model = ref<ModelApi.ModelVO>() // 流程模型的信息
67
c9a6f7 68 // 初始化 bpmnInstances
H 69 const initBpmnInstances = () => {
70   if (!modeler.value) return false
71   try {
72     const instances = {
73       modeler: modeler.value,
74       modeling: modeler.value.get('modeling'),
75       moddle: modeler.value.get('moddle'),
76       eventBus: modeler.value.get('eventBus'),
77       bpmnFactory: modeler.value.get('bpmnFactory'),
78       elementFactory: modeler.value.get('elementFactory'),
79       elementRegistry: modeler.value.get('elementRegistry'),
80       replace: modeler.value.get('replace'),
81       selection: modeler.value.get('selection')
82     }
83
84     // 检查所有实例是否都存在
85     return Object.values(instances).every((instance) => instance)
86   } catch (error) {
87     console.error('初始化 bpmnInstances 失败:', error)
88     return false
89   }
90 }
91
820397 92 /** 初始化 modeler */
c9a6f7 93 const initModeler = async (item) => {
H 94   try {
820397 95     modeler.value = item
c9a6f7 96     // 等待 modeler 初始化完成
H 97     await nextTick()
98
99     // 确保 modeler 的所有实例都已经准备好
100     if (initBpmnInstances()) {
101       isModelerReady.value = true
102       emit('init-finished')
103
104       // 初始化完成后,设置初始值
105       if (props.modelId) {
106         // 编辑模式
107         const data = await ModelApi.getModel(props.modelId)
108         model.value = {
109           ...data,
110           bpmnXml: undefined // 清空 bpmnXml 属性
111         }
112         xmlString.value = data.bpmnXml || getDefaultBpmnXml(data.key, data.name)
113       } else if (props.modelKey && props.modelName) {
114         // 新建模式
115         xmlString.value = props.value || getDefaultBpmnXml(props.modelKey, props.modelName)
116         model.value = {
117           key: props.modelKey,
118           name: props.modelName
119         } as ModelApi.ModelVO
120       }
121
122       // 导入XML并刷新视图
123       await nextTick()
124       try {
125         await modeler.value.importXML(xmlString.value)
126         if (processDesigner.value?.refresh) {
127           processDesigner.value.refresh()
128         }
129       } catch (error) {
130         console.error('导入XML失败:', error)
131       }
132     } else {
133       console.error('modeler 实例未完全初始化')
134     }
135   } catch (error) {
136     console.error('初始化 modeler 失败:', error)
137   }
138 }
139
140 /** 获取默认的BPMN XML */
141 const getDefaultBpmnXml = (key: string, name: string) => {
142   return `<?xml version="1.0" encoding="UTF-8"?>
143 <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.activiti.org/processdef">
144   <process id="${key}" name="${name}" isExecutable="true" />
145   <bpmndi:BPMNDiagram id="BPMNDiagram">
146     <bpmndi:BPMNPlane id="${key}_di" bpmnElement="${key}" />
147   </bpmndi:BPMNDiagram>
148 </definitions>`
820397 149 }
H 150
151 /** 添加/修改模型 */
3e359e 152 const save = async (bpmnXml: string) => {
c9a6f7 153   try {
H 154     xmlString.value = bpmnXml
155     if (props.modelId) {
156       // 编辑模式
157       const data = {
158         ...model.value,
159         bpmnXml: bpmnXml
160       } as unknown as ModelApi.ModelVO
161       await ModelApi.updateModelBpmn(data)
162       emit('success')
163     } else {
164       // 新建模式,直接返回XML
165       emit('success', bpmnXml)
166     }
167   } catch (error) {
168     console.error('保存失败:', error)
169     message.error('保存失败')
820397 170   }
H 171 }
172
c9a6f7 173 // 监听 key、name 和 value 的变化
H 174 watch(
175   [() => props.modelKey, () => props.modelName, () => props.value],
176   async ([newKey, newName, newValue]) => {
177     if (!props.modelId && isModelerReady.value) {
178       let shouldRefresh = false
179
180       if (newKey && newName) {
181         const newXml = newValue || getDefaultBpmnXml(newKey, newName)
182         if (newXml !== xmlString.value) {
183           xmlString.value = newXml
184           shouldRefresh = true
185         }
186         model.value = {
187           ...model.value,
188           key: newKey,
189           name: newName
190         } as ModelApi.ModelVO
191       } else if (newValue && newValue !== xmlString.value) {
192         xmlString.value = newValue
193         shouldRefresh = true
194       }
195
196       if (shouldRefresh) {
197         // 确保更新后重新渲染
198         await nextTick()
199         if (processDesigner.value?.refresh) {
200           try {
201             await modeler.value?.importXML(xmlString.value)
202             processDesigner.value.refresh()
203           } catch (error) {
204             console.error('导入XML失败:', error)
205           }
206         }
207       }
208     }
209   },
210   { deep: true }
211 )
212
213 // 在组件卸载时清理
214 onBeforeUnmount(() => {
215   isModelerReady.value = false
216   modeler.value = null
217   // 清理全局实例
218   const w = window as any
219   if (w.bpmnInstances) {
220     w.bpmnInstances = null
221   }
222 })
223
224 /** 获取 XML 字符串 */
225 const saveXML = async () => {
226   if (!modeler.value) {
227     return { xml: xmlString.value }
228   }
229   try {
230     const result = await modeler.value.saveXML({ format: true })
231     xmlString.value = result.xml
232     return result
233   } catch (error) {
234     console.error('获取XML失败:', error)
235     return { xml: xmlString.value }
236   }
820397 237 }
H 238
c9a6f7 239 /** 获取SVG字符串 */
H 240 const saveSVG = async () => {
241   if (!modeler.value) {
242     return { svg: undefined }
820397 243   }
c9a6f7 244   try {
H 245     return await modeler.value.saveSVG()
246   } catch (error) {
247     console.error('获取SVG失败:', error)
248     return { svg: undefined }
820397 249   }
c9a6f7 250 }
9259c2 251
c9a6f7 252 /** 刷新视图 */
H 253 const refresh = async () => {
254   if (processDesigner.value?.refresh && modeler.value) {
255     try {
256       await modeler.value.importXML(xmlString.value)
257       processDesigner.value.refresh()
258     } catch (error) {
259       console.error('刷新视图失败:', error)
260     }
9259c2 261   }
c9a6f7 262 }
9259c2 263
c9a6f7 264 // 暴露必要的属性和方法给父组件
H 265 defineExpose({
266   modeler,
267   isModelerReady,
268   saveXML,
269   saveSVG,
270   refresh
820397 271 })
H 272 </script>
273 <style lang="scss">
274 .process-panel__container {
275   position: absolute;
c9a6f7 276   top: 172px;
H 277   right: 70px;
820397 278 }
H 279 </style>