SignalAndMessage.vue 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. <template>
  2. <div class="panel-tab__content">
  3. <div class="panel-tab__content--title">
  4. <span><Icon icon="ep:menu" style="margin-right: 8px; color: #555" />消息列表</span>
  5. <XButton type="primary" title="创建新消息" preIcon="ep:plus" @click="openModel('message')" />
  6. </div>
  7. <el-table :data="messageList" border>
  8. <el-table-column type="index" label="序号" width="60px" />
  9. <el-table-column label="消息ID" prop="id" min-width="120px" show-overflow-tooltip />
  10. <el-table-column label="消息名称" prop="name" min-width="120px" show-overflow-tooltip />
  11. <el-table-column label="操作" width="110px">
  12. <!-- 补充“编辑”、“移除”功能。相关 issue:https://github.com/YunaiV/yudao-cloud/issues/270 -->
  13. <template #default="scope">
  14. <el-button link @click="openEditModel('message', scope.row, scope.$index)" size="small">
  15. 编辑
  16. </el-button>
  17. <el-divider direction="vertical" />
  18. <el-button
  19. link
  20. size="small"
  21. style="color: #ff4d4f"
  22. @click="removeObject('message', scope.row)"
  23. >
  24. 移除
  25. </el-button>
  26. </template>
  27. </el-table-column>
  28. </el-table>
  29. <div
  30. class="panel-tab__content--title"
  31. style="padding-top: 8px; margin-top: 8px; border-top: 1px solid #eee"
  32. >
  33. <span><Icon icon="ep:menu" style="margin-right: 8px; color: #555" />信号列表</span>
  34. <XButton type="primary" title="创建新信号" preIcon="ep:plus" @click="openModel('signal')" />
  35. </div>
  36. <el-table :data="signalList" border>
  37. <el-table-column type="index" label="序号" width="60px" />
  38. <el-table-column label="信号ID" prop="id" min-width="120px" show-overflow-tooltip />
  39. <el-table-column label="信号名称" prop="name" min-width="120px" show-overflow-tooltip />
  40. <el-table-column label="操作" width="110px">
  41. <template #default="scope">
  42. <el-button link @click="openEditModel('signal', scope.row, scope.$index)" size="small">
  43. 编辑
  44. </el-button>
  45. <el-divider direction="vertical" />
  46. <el-button
  47. link
  48. size="small"
  49. style="color: #ff4d4f"
  50. @click="removeObject('signal', scope.row)"
  51. >
  52. 移除
  53. </el-button>
  54. </template>
  55. </el-table-column>
  56. </el-table>
  57. <el-dialog
  58. v-model="dialogVisible"
  59. :title="modelConfig.title"
  60. :close-on-click-modal="false"
  61. width="400px"
  62. append-to-body
  63. destroy-on-close
  64. >
  65. <el-form :model="modelObjectForm" label-width="90px">
  66. <el-form-item :label="modelConfig.idLabel">
  67. <el-input v-model="modelObjectForm.id" clearable />
  68. </el-form-item>
  69. <el-form-item :label="modelConfig.nameLabel">
  70. <el-input v-model="modelObjectForm.name" clearable />
  71. </el-form-item>
  72. </el-form>
  73. <template #footer>
  74. <el-button @click="dialogVisible = false">取 消</el-button>
  75. <el-button type="primary" @click="addNewObject">保 存</el-button>
  76. </template>
  77. </el-dialog>
  78. </div>
  79. </template>
  80. <script lang="ts" setup>
  81. import { ElMessageBox } from 'element-plus'
  82. defineOptions({ name: 'SignalAndMassage' })
  83. const message = useMessage()
  84. const signalList = ref<any[]>([])
  85. const messageList = ref<any[]>([])
  86. const dialogVisible = ref(false)
  87. const modelType = ref('')
  88. const modelObjectForm = ref<any>({})
  89. const rootElements = ref()
  90. const messageIdMap = ref()
  91. const signalIdMap = ref()
  92. const editingIndex = ref(-1) // 正在编辑的索引,-1 表示新建
  93. const modelConfig = computed(() => {
  94. const isEdit = editingIndex.value !== -1
  95. if (modelType.value === 'message') {
  96. return {
  97. title: isEdit ? '编辑消息' : '创建消息',
  98. idLabel: '消息ID',
  99. nameLabel: '消息名称'
  100. }
  101. } else {
  102. return {
  103. title: isEdit ? '编辑信号' : '创建信号',
  104. idLabel: '信号ID',
  105. nameLabel: '信号名称'
  106. }
  107. }
  108. })
  109. const bpmnInstances = () => (window as any)?.bpmnInstances
  110. // 生成规范化的ID
  111. const generateStandardId = (type: string): string => {
  112. const prefix = type === 'message' ? 'Message_' : 'Signal_'
  113. const timestamp = Date.now()
  114. const random = Math.random().toString(36).substring(2, 6).toUpperCase()
  115. return `${prefix}${timestamp}_${random}`
  116. }
  117. const initDataList = () => {
  118. console.log(window, 'window')
  119. rootElements.value = bpmnInstances().modeler.getDefinitions().rootElements
  120. messageIdMap.value = {}
  121. signalIdMap.value = {}
  122. messageList.value = []
  123. signalList.value = []
  124. rootElements.value.forEach((el) => {
  125. if (el.$type === 'bpmn:Message') {
  126. messageIdMap.value[el.id] = true
  127. messageList.value.push({ ...el })
  128. }
  129. if (el.$type === 'bpmn:Signal') {
  130. signalIdMap.value[el.id] = true
  131. signalList.value.push({ ...el })
  132. }
  133. })
  134. }
  135. const openModel = (type) => {
  136. modelType.value = type
  137. editingIndex.value = -1
  138. modelObjectForm.value = {
  139. id: generateStandardId(type),
  140. name: ''
  141. }
  142. dialogVisible.value = true
  143. }
  144. const openEditModel = (type, row, index) => {
  145. modelType.value = type
  146. editingIndex.value = index
  147. modelObjectForm.value = { ...row }
  148. dialogVisible.value = true
  149. }
  150. const addNewObject = () => {
  151. if (modelType.value === 'message') {
  152. // 编辑模式
  153. if (editingIndex.value !== -1) {
  154. const targetMessage = messageList.value[editingIndex.value]
  155. // 查找 rootElements 中的原始对象
  156. const rootMessage = rootElements.value.find(
  157. (el) => el.$type === 'bpmn:Message' && el.id === targetMessage.id
  158. )
  159. if (rootMessage) {
  160. rootMessage.id = modelObjectForm.value.id
  161. rootMessage.name = modelObjectForm.value.name
  162. }
  163. } else {
  164. // 新建模式
  165. if (messageIdMap.value[modelObjectForm.value.id]) {
  166. message.error('该消息已存在,请修改id后重新保存')
  167. return
  168. }
  169. const messageRef = bpmnInstances().moddle.create('bpmn:Message', modelObjectForm.value)
  170. rootElements.value.push(messageRef)
  171. }
  172. } else {
  173. // 编辑模式
  174. if (editingIndex.value !== -1) {
  175. const targetSignal = signalList.value[editingIndex.value]
  176. // 查找 rootElements 中的原始对象
  177. const rootSignal = rootElements.value.find(
  178. (el) => el.$type === 'bpmn:Signal' && el.id === targetSignal.id
  179. )
  180. if (rootSignal) {
  181. rootSignal.id = modelObjectForm.value.id
  182. rootSignal.name = modelObjectForm.value.name
  183. }
  184. } else {
  185. // 新建模式
  186. if (signalIdMap.value[modelObjectForm.value.id]) {
  187. message.error('该信号已存在,请修改id后重新保存')
  188. return
  189. }
  190. const signalRef = bpmnInstances().moddle.create('bpmn:Signal', modelObjectForm.value)
  191. rootElements.value.push(signalRef)
  192. }
  193. }
  194. dialogVisible.value = false
  195. // 触发建模器更新以保存更改
  196. saveChanges()
  197. initDataList()
  198. }
  199. const removeObject = (type, row) => {
  200. ElMessageBox.confirm(`确认移除该${type === 'message' ? '消息' : '信号'}吗?`, '提示', {
  201. confirmButtonText: '确 认',
  202. cancelButtonText: '取 消'
  203. })
  204. .then(() => {
  205. // 从 rootElements 中移除
  206. const targetType = type === 'message' ? 'bpmn:Message' : 'bpmn:Signal'
  207. const elementIndex = rootElements.value.findIndex(
  208. (el) => el.$type === targetType && el.id === row.id
  209. )
  210. if (elementIndex !== -1) {
  211. rootElements.value.splice(elementIndex, 1)
  212. }
  213. // 触发建模器更新以保存更改
  214. saveChanges()
  215. // 刷新列表
  216. initDataList()
  217. message.success('移除成功')
  218. })
  219. .catch(() => console.info('操作取消'))
  220. }
  221. // 触发建模器更新以保存更改
  222. const saveChanges = () => {
  223. const modeler = bpmnInstances().modeler
  224. if (!modeler) return
  225. try {
  226. // 获取 canvas,通过它来触发图表的重新渲染
  227. const canvas = modeler.get('canvas')
  228. // 获取根元素(Process)
  229. const rootElement = canvas.getRootElement()
  230. // 触发 changed 事件,通知建模器数据已更改
  231. const eventBus = modeler.get('eventBus')
  232. if (eventBus) {
  233. eventBus.fire('root.added', { element: rootElement })
  234. eventBus.fire('elements.changed', { elements: [rootElement] })
  235. }
  236. // 标记建模器为已修改状态
  237. const commandStack = modeler.get('commandStack')
  238. if (commandStack && commandStack._stack) {
  239. // 添加一个空命令以标记为已修改
  240. commandStack.execute('element.updateProperties', {
  241. element: rootElement,
  242. properties: {}
  243. })
  244. }
  245. } catch (error) {
  246. console.warn('保存更改时出错:', error)
  247. }
  248. }
  249. onMounted(() => {
  250. initDataList()
  251. })
  252. </script>