ProcessViewer.vue 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. <template>
  2. <div class="my-process-designer">
  3. <div class="my-process-designer__container">
  4. <div class="my-process-designer__canvas" ref="bpmn-canvas"></div>
  5. </div>
  6. </div>
  7. </template>
  8. <script>
  9. import BpmnViewer from "bpmn-js/lib/Viewer";
  10. import DefaultEmptyXML from "./plugins/defaultEmpty";
  11. export default {
  12. name: "MyProcessViewer",
  13. componentName: "MyProcessViewer",
  14. props: {
  15. value: String, // xml 字符串
  16. prefix: {
  17. type: String,
  18. default: "camunda"
  19. },
  20. taskData: {
  21. type: Array,
  22. default: () => []
  23. }
  24. },
  25. data() {
  26. return {
  27. xml: '',
  28. tasks: [],
  29. };
  30. },
  31. mounted() {
  32. this.xml = this.value;
  33. this.tasks = this.taskData;
  34. // 初始化
  35. this.initBpmnModeler();
  36. this.createNewDiagram(this.xml);
  37. this.$once("hook:beforeDestroy", () => {
  38. if (this.bpmnModeler) this.bpmnModeler.destroy();
  39. this.$emit("destroy", this.bpmnModeler);
  40. this.bpmnModeler = null;
  41. });
  42. },
  43. watch: {
  44. value: function (newValue) { // 在 xmlString 发生变化时,重新创建,从而绘制流程图
  45. this.xml = newValue;
  46. this.createNewDiagram(this.xml);
  47. },
  48. taskData: function (newTaskData) {
  49. this.tasks = newTaskData;
  50. this.createNewDiagram(this.xml);
  51. }
  52. },
  53. methods: {
  54. initBpmnModeler() {
  55. if (this.bpmnModeler) return;
  56. this.bpmnModeler = new BpmnViewer({
  57. container: this.$refs["bpmn-canvas"],
  58. bpmnRenderer: {
  59. }
  60. })
  61. },
  62. /* 创建新的流程图 */
  63. async createNewDiagram(xml) {
  64. // 将字符串转换成图显示出来
  65. let newId = `Process_${new Date().getTime()}`;
  66. let newName = `业务流程_${new Date().getTime()}`;
  67. let xmlString = xml || DefaultEmptyXML(newId, newName, this.prefix);
  68. try {
  69. // console.log(this.bpmnModeler.importXML);
  70. let { warnings } = await this.bpmnModeler.importXML(xmlString);
  71. if (warnings && warnings.length) {
  72. warnings.forEach(warn => console.warn(warn));
  73. }
  74. // 高亮流程图
  75. await this.highlightDiagram();
  76. } catch (e) {
  77. console.error(e);
  78. // console.error(`[Process Designer Warn]: ${e?.message || e}`);
  79. }
  80. },
  81. /* 高亮流程图 */
  82. async highlightDiagram() {
  83. // let tasks = this.tasks.filter(task => {
  84. // if (task.type !== 'sequenceFlow') { // 去除连线元素
  85. // return true;
  86. // }
  87. // });
  88. let tasks = this.tasks;
  89. if (tasks.length === 0) {
  90. return;
  91. }
  92. // 参考自 https://gitee.com/tony2y/RuoYi-flowable/blob/master/ruoyi-ui/src/components/Process/index.vue#L222 实现
  93. let canvas = this.bpmnModeler.get('canvas');
  94. this.bpmnModeler.getDefinitions().rootElements[0].flowElements?.forEach(n => {
  95. let completeTask = tasks.find(m => m.key === n.id)
  96. let todoTask = tasks.find(m => !m.endTime)
  97. let endTask = tasks[tasks.length - 1]
  98. if (n.$type === 'bpmn:UserTask') { // 用户任务
  99. if (completeTask) {
  100. canvas.addMarker(n.id, completeTask.endTime ? 'highlight' : 'highlight-todo');
  101. // console.log(n.id + ' : ' + (completeTask.endTime ? 'highlight' : 'highlight-todo'));
  102. n.outgoing?.forEach(nn => {
  103. let targetTask = tasks.find(m => m.key === nn.targetRef.id)
  104. if (targetTask) {
  105. canvas.addMarker(nn.id, targetTask.endTime ? 'highlight' : 'highlight-todo');
  106. } else if (nn.targetRef.$type === 'bpmn:ExclusiveGateway') {
  107. // canvas.addMarker(nn.id, 'highlight');
  108. canvas.addMarker(nn.id, completeTask.endTime ? 'highlight' : 'highlight-todo');
  109. canvas.addMarker(nn.targetRef.id, completeTask.endTime ? 'highlight' : 'highlight-todo');
  110. } else if (nn.targetRef.$type === 'bpmn:EndEvent') {
  111. if (!todoTask && endTask.key === n.id) {
  112. canvas.addMarker(nn.id, 'highlight');
  113. canvas.addMarker(nn.targetRef.id, 'highlight');
  114. }
  115. if (!completeTask.endTime) {
  116. canvas.addMarker(nn.id, 'highlight-todo');
  117. canvas.addMarker(nn.targetRef.id, 'highlight-todo');
  118. }
  119. }
  120. });
  121. }
  122. } else if (n.$type === 'bpmn:ExclusiveGateway') { // 排它网关
  123. n.outgoing?.forEach(nn => {
  124. let targetTask = tasks.find(m => m.key === nn.targetRef.id)
  125. if (targetTask) {
  126. canvas.addMarker(nn.id, targetTask.endTime ? 'highlight' : 'highlight-todo');
  127. }
  128. })
  129. } else if (n.$type === 'bpmn:ParallelGateway') { // 并行网关
  130. if (completeTask) {
  131. canvas.addMarker(n.id, completeTask.endTime ? 'highlight' : 'highlight-todo')
  132. n.outgoing?.forEach(nn => {
  133. const targetTask = this.taskList.find(m => m.key === nn.targetRef.id)
  134. if (targetTask) {
  135. canvas.addMarker(nn.id, targetTask.endTime ? 'highlight' : 'highlight-todo')
  136. canvas.addMarker(nn.targetRef.id, targetTask.endTime ? 'highlight' : 'highlight-todo')
  137. }
  138. })
  139. }
  140. } else if (n.$type === 'bpmn:StartEvent') { // 开始节点
  141. n.outgoing?.forEach(nn => {
  142. let completeTask = tasks.find(m => m.key === nn.targetRef.id)
  143. if (completeTask) {
  144. canvas.addMarker(nn.id, 'highlight');
  145. canvas.addMarker(n.id, 'highlight');
  146. return
  147. }
  148. });
  149. } else if (n.$type === 'bpmn:EndEvent') { // 结束节点
  150. if (endTask.key === n.id && endTask.endTime) {
  151. canvas.addMarker(n.id, 'highlight')
  152. return
  153. }
  154. }
  155. })
  156. }
  157. }
  158. };
  159. </script>
  160. <style>
  161. .highlight.djs-shape .djs-visual > :nth-child(1) {
  162. fill: green !important;
  163. stroke: green !important;
  164. fill-opacity: 0.2 !important;
  165. }
  166. .highlight.djs-shape .djs-visual > :nth-child(2) {
  167. fill: green !important;
  168. }
  169. .highlight.djs-shape .djs-visual > path {
  170. fill: green !important;
  171. fill-opacity: 0.2 !important;
  172. stroke: green !important;
  173. }
  174. .highlight.djs-connection > .djs-visual > path {
  175. stroke: green !important;
  176. }
  177. .highlight-todo.djs-connection > .djs-visual > path {
  178. stroke: orange !important;
  179. stroke-dasharray: 4px !important;
  180. fill-opacity: 0.2 !important;
  181. }
  182. .highlight-todo.djs-shape .djs-visual > :nth-child(1) {
  183. fill: orange !important;
  184. stroke: orange !important;
  185. stroke-dasharray: 4px !important;
  186. fill-opacity: 0.2 !important;
  187. }
  188. .highlight:not(.djs-connection) .djs-visual > :nth-child(1) {
  189. fill: green !important; /* color elements as green */
  190. }
  191. /deep/.highlight.djs-shape .djs-visual > :nth-child(1) {
  192. fill: green !important;
  193. stroke: green !important;
  194. fill-opacity: 0.2 !important;
  195. }
  196. /deep/.highlight.djs-shape .djs-visual > :nth-child(2) {
  197. fill: green !important;
  198. }
  199. /deep/.highlight.djs-shape .djs-visual > path {
  200. fill: green !important;
  201. fill-opacity: 0.2 !important;
  202. stroke: green !important;
  203. }
  204. /deep/.highlight.djs-connection > .djs-visual > path {
  205. stroke: green !important;
  206. }
  207. /deep/.highlight-todo.djs-connection > .djs-visual > path {
  208. stroke: orange !important;
  209. stroke-dasharray: 4px !important;
  210. fill-opacity: 0.2 !important;
  211. marker-end: url(#sequenceflow-end-_E7DFDF-_E7DFDF-803g1kf6zwzmcig1y2ulm5egr);
  212. }
  213. /deep/.highlight-todo.djs-shape .djs-visual > :nth-child(1) {
  214. fill: orange !important;
  215. stroke: orange !important;
  216. stroke-dasharray: 4px !important;
  217. fill-opacity: 0.2 !important;
  218. }
  219. </style>