ParallelNode.vue 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. <template>
  2. <div class="branch-node-wrapper">
  3. <div class="branch-node-container">
  4. <div class="branch-node-add" @click="addCondition">添加分支</div>
  5. <div
  6. class="branch-node-item"
  7. v-for="(item, index) in currentNode.conditionNodes"
  8. :key="index"
  9. >
  10. <template v-if="index == 0">
  11. <div class="branch-line-first-top"></div>
  12. <div class="branch-line-first-bottom"></div>
  13. </template>
  14. <template v-if="index + 1 == currentNode.conditionNodes?.length">
  15. <div class="branch-line-last-top"></div>
  16. <div class="branch-line-last-bottom"></div>
  17. </template>
  18. <div class="node-wrapper">
  19. <div class="node-container">
  20. <div class="node-box">
  21. <div class="branch-node-title-container">
  22. <div v-if="showInputs[index]">
  23. <input
  24. type="text"
  25. class="input-max-width editable-title-input"
  26. @blur="blurEvent(index)"
  27. v-mountedFocus
  28. v-model="item.name"
  29. />
  30. </div>
  31. <div v-else class="branch-title" @click="clickEvent(index)"> {{ item.name }} </div>
  32. <div class="branch-priority">无优先级</div>
  33. </div>
  34. <div class="branch-node-content" @click="conditionNodeConfig(item.id)">
  35. <div class="branch-node-text" :title="item.showText" v-if="item.showText">
  36. {{ item.showText }}
  37. </div>
  38. <div class="branch-node-text" v-else>
  39. {{ NODE_DEFAULT_TEXT.get(NodeType.CONDITION_NODE) }}
  40. </div>
  41. </div>
  42. <div class="node-toolbar">
  43. <div class="toolbar-icon">
  44. <Icon
  45. color="#0089ff"
  46. icon="ep:circle-close-filled"
  47. :size="18"
  48. @click="deleteCondition(index)"
  49. />
  50. </div>
  51. </div>
  52. <!-- <div
  53. class="branch-node-move move-node-left"
  54. v-if="index != 0 && index + 1 !== currentNode.conditionNodes?.length" @click="moveNode(index, -1)">
  55. <Icon icon="ep:arrow-left" />
  56. </div> -->
  57. <!-- <div
  58. class="branch-node-move move-node-right"
  59. v-if="currentNode.conditionNodes && index < currentNode.conditionNodes.length - 2"
  60. @click="moveNode(index, 1)">
  61. <Icon icon="ep:arrow-right" />
  62. </div> -->
  63. </div>
  64. <NodeHandler v-model:child-node="item.childNode" />
  65. </div>
  66. </div>
  67. <!-- 递归显示子节点 -->
  68. <ProcessNodeTree
  69. v-if="item && item.childNode"
  70. :parent-node="item"
  71. v-model:flow-node="item.childNode"
  72. @find:recursive-find-parent-node="recursiveFindParentNode"
  73. />
  74. </div>
  75. </div>
  76. <NodeHandler v-if="currentNode" v-model:child-node="currentNode.childNode" />
  77. </div>
  78. </template>
  79. <script setup lang="ts">
  80. import NodeHandler from '../NodeHandler.vue'
  81. import ProcessNodeTree from '../ProcessNodeTree.vue'
  82. import { SimpleFlowNode, NodeType, NODE_DEFAULT_TEXT } from '../consts'
  83. import { generateUUID } from '@/utils'
  84. const { proxy } = getCurrentInstance() as any
  85. defineOptions({
  86. name: 'ParallelNode'
  87. })
  88. const props = defineProps({
  89. flowNode: {
  90. type: Object as () => SimpleFlowNode,
  91. required: true
  92. }
  93. })
  94. // 定义事件,更新父组件
  95. const emits = defineEmits<{
  96. 'update:modelValue': [node: SimpleFlowNode | undefined]
  97. 'find:parentNode': [nodeList: SimpleFlowNode[], nodeType: number]
  98. 'find:recursiveFindParentNode': [
  99. nodeList: SimpleFlowNode[],
  100. curentNode: SimpleFlowNode,
  101. nodeType: number
  102. ]
  103. }>()
  104. const currentNode = ref<SimpleFlowNode>(props.flowNode)
  105. watch(
  106. () => props.flowNode,
  107. (newValue) => {
  108. currentNode.value = newValue
  109. }
  110. )
  111. const showInputs = ref<boolean[]>([])
  112. // 失去焦点
  113. const blurEvent = (index: number) => {
  114. showInputs.value[index] = false
  115. const conditionNode = currentNode.value.conditionNodes?.at(index) as SimpleFlowNode
  116. conditionNode.name = conditionNode.name || `并行${index + 1}`
  117. }
  118. // 点击条件名称
  119. const clickEvent = (index: number) => {
  120. showInputs.value[index] = true
  121. }
  122. const conditionNodeConfig = (nodeId: string) => {
  123. const conditionNode = proxy.$refs[nodeId][0]
  124. conditionNode.open()
  125. }
  126. // 新增条件
  127. const addCondition = () => {
  128. const conditionNodes = currentNode.value.conditionNodes
  129. if (conditionNodes) {
  130. const len = conditionNodes.length
  131. let lastIndex = len - 1
  132. const conditionData: SimpleFlowNode = {
  133. id: 'Flow_' + generateUUID(),
  134. name: '并行' + len,
  135. showText: '无需配置条件同时执行',
  136. type: NodeType.CONDITION_NODE,
  137. childNode: undefined,
  138. conditionNodes: []
  139. }
  140. conditionNodes.splice(lastIndex, 0, conditionData)
  141. }
  142. }
  143. // 删除条件
  144. const deleteCondition = (index: number) => {
  145. const conditionNodes = currentNode.value.conditionNodes
  146. if (conditionNodes) {
  147. conditionNodes.splice(index, 1)
  148. if (conditionNodes.length == 1) {
  149. const childNode = currentNode.value.childNode
  150. // 更新此节点为后续孩子节点
  151. emits('update:modelValue', childNode)
  152. }
  153. }
  154. }
  155. // 递归从父节点中查询匹配的节点
  156. const recursiveFindParentNode = (
  157. nodeList: SimpleFlowNode[],
  158. node: SimpleFlowNode,
  159. nodeType: number
  160. ) => {
  161. if (!node || node.type === NodeType.START_EVENT_NODE) {
  162. return
  163. }
  164. if (node.type === nodeType) {
  165. nodeList.push(node)
  166. }
  167. // 条件节点 (NodeType.CONDITION_NODE) 比较特殊。需要调用其父节点并行节点(NodeType.PARALLEL_NODE) 继续查找
  168. emits('find:parentNode', nodeList, nodeType)
  169. }
  170. </script>