utils.ts 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. // 根据传入的elements、node节点、edge节点对elements元素进行再计算
  2. import { ArrowHeadType, isEdge, isNode, Position } from 'react-flow-renderer'
  3. import { buildUUID } from '@/utils/uuid'
  4. import type { Edge, Elements, Node, XYPosition } from 'react-flow-renderer'
  5. import { ParticipantMode } from './enum'
  6. export enum genreateElementEnum {
  7. ADD = 'add',
  8. DEL = 'del'
  9. }
  10. /** 通过传入的新的node节点以及当前操作的edge构建新的elements */
  11. function buildWithNewNode(elements: Elements, newNode?: Node | null, oldEdge?: Edge | null) {
  12. if (!newNode || !oldEdge) return elements
  13. // 取出当前操作edge的数据
  14. const { id: edgeId, source: edgeSid, target: edgeTid } = oldEdge
  15. // 原来node节点中的sort
  16. const sort = elements.find(item => item.id === edgeTid)?.data?.sort
  17. // console.log(sort)
  18. let oldIdx
  19. const newElements: Elements = [...elements].map((item, idx) => {
  20. if (isNode(item)) {
  21. if (item.data?.sort >= sort) {
  22. return { ...item, data: { ...item.data, sort: item.data.sort + 1 } }
  23. }
  24. if (item.id === edgeTid) {
  25. oldIdx = idx
  26. }
  27. return item
  28. } else if (isEdge(item) && item.id === edgeId) {
  29. // 修改原来edge的source
  30. const edge = { ...item, source: newNode.id }
  31. return edge
  32. }
  33. return item
  34. })
  35. // 添加新的node和edge
  36. // TODO: 在当前oldEdge target指向的节点前插入node
  37. newElements.splice(oldIdx + 1, 0, {
  38. ...newNode,
  39. position: { x: 500, y: sort * 200 },
  40. type: 'common',
  41. data: { sort }
  42. })
  43. newElements.push({
  44. id: buildUUID(),
  45. source: edgeSid,
  46. target: newNode.id,
  47. type: 'common',
  48. arrowHeadType: ArrowHeadType.Arrow
  49. })
  50. return newElements.map(item => {
  51. if (isNode(item)) {
  52. return { ...item, position: { ...item.position, y: item.data?.sort * 200 } }
  53. }
  54. return item
  55. })
  56. }
  57. /** 传入需要删除的node构建新的elements */
  58. function buildWithDeletedNode(elements: Elements, deletedNode?: Node | null) {
  59. if (!deletedNode) return elements
  60. const {
  61. id,
  62. data: { sort }
  63. } = deletedNode
  64. // 1.先找到node关联的的那两条线
  65. const upEdge: Edge = elements.find(item => isEdge(item) && item.target === id)
  66. const downEdge: Edge = elements.find(item => isEdge(item) && item.source === id)
  67. // 2. 删除对应的edge以及node以及重写sort
  68. return elements
  69. .filter(item => item.id !== id && item.id !== downEdge.id)
  70. .map(item => {
  71. if (isNode(item) && item.data?.sort > sort)
  72. return {
  73. ...item,
  74. data: { sort: item.data?.sort - 1 },
  75. position: { ...item.position, y: (item.data?.sort - 1) * 200 }
  76. }
  77. if (isEdge(item) && item.id === upEdge.id) {
  78. return { ...item, target: downEdge.target }
  79. }
  80. return item
  81. })
  82. }
  83. export const generateElements = (opreate: genreateElementEnum, elements: Elements) =>
  84. opreate === genreateElementEnum.ADD
  85. ? (newNode?: Node | null, oldEdge?: Edge | null) => buildWithNewNode(elements, newNode, oldEdge)
  86. : (deletedNode?: Node | null) => buildWithDeletedNode(elements, deletedNode)
  87. // export function generateElements(elements: Elements, newNode?: Node | null, oldEdge?: Edge | null) {
  88. // }
  89. // this helper function returns the intersection point
  90. // of the line between the center of the intersectionNode and the target node
  91. function getNodeIntersection(intersectionNode: Node, targetNode: Node): XYPosition {
  92. // https://math.stackexchange.com/questions/1724792/an-algorithm-for-finding-the-intersection-point-between-a-center-of-vision-and-a
  93. const {
  94. width: intersectionNodeWidth,
  95. height: intersectionNodeHeight,
  96. position: intersectionNodePosition
  97. } = intersectionNode.__rf
  98. const targetPosition = targetNode.__rf.position
  99. const w = intersectionNodeWidth / 2
  100. const h = intersectionNodeHeight / 2
  101. const x2 = intersectionNodePosition.x + w
  102. const y2 = intersectionNodePosition.y + h
  103. const x1 = targetPosition.x + w
  104. const y1 = targetPosition.y + h
  105. const xx1 = (x1 - x2) / (2 * w) - (y1 - y2) / (2 * h)
  106. const yy1 = (x1 - x2) / (2 * w) + (y1 - y2) / (2 * h)
  107. const a = 1 / (Math.abs(xx1) + Math.abs(yy1))
  108. const xx3 = a * xx1
  109. const yy3 = a * yy1
  110. const x = w * (xx3 + yy3) + x2
  111. const y = h * (-xx3 + yy3) + y2
  112. return { x, y }
  113. }
  114. // returns the position (top,right,bottom or right) passed node compared to the intersection point
  115. function getEdgePosition(node: Node, intersectionPoint: XYPosition) {
  116. const n = { ...node.__rf.position, ...node.__rf }
  117. const nx = Math.round(n.x)
  118. const ny = Math.round(n.y)
  119. const px = Math.round(intersectionPoint.x)
  120. const py = Math.round(intersectionPoint.y)
  121. if (px <= nx + 1) {
  122. return Position.Left
  123. }
  124. if (px >= nx + n.width - 1) {
  125. return Position.Right
  126. }
  127. if (py <= ny + 1) {
  128. return Position.Top
  129. }
  130. if (py >= n.y + n.height - 1) {
  131. return Position.Bottom
  132. }
  133. return Position.Top
  134. }
  135. // returns the parameters (sx, sy, tx, ty, sourcePos, targetPos) you need to create an edge
  136. export function getEdgeParams(source: Node, target: Node) {
  137. const sourceIntersectionPoint = getNodeIntersection(source, target)
  138. const targetIntersectionPoint = getNodeIntersection(target, source)
  139. const sourcePos = getEdgePosition(source, sourceIntersectionPoint)
  140. const targetPos = getEdgePosition(target, targetIntersectionPoint)
  141. return {
  142. sx: sourceIntersectionPoint.x,
  143. sy: sourceIntersectionPoint.y,
  144. tx: targetIntersectionPoint.x,
  145. ty: targetIntersectionPoint.y,
  146. sourcePos,
  147. targetPos
  148. }
  149. }
  150. export const participantSchema = {
  151. type: 'object',
  152. properties: {
  153. participantMode: {
  154. type: 'string',
  155. title: '',
  156. required: true,
  157. 'x-decorator': 'FormItem',
  158. 'x-component': 'Select',
  159. default: ParticipantMode.ACCOUNT,
  160. enum: [
  161. { label: '仅用户模式', value: ParticipantMode.ACCOUNT }
  162. // { label: '角色模式', value: ParticipantMode.ROLE }
  163. ],
  164. 'x-index': 1
  165. },
  166. name: {
  167. type: 'string',
  168. title: '',
  169. required: true,
  170. 'x-decorator': 'FormItem',
  171. 'x-component': 'Input',
  172. 'x-display': 'none',
  173. 'x-index': 4
  174. },
  175. institutionID: {
  176. type: 'string',
  177. title: '',
  178. required: true,
  179. 'x-decorator': 'FormItem',
  180. 'x-component': 'Select',
  181. 'x-component-props': {
  182. placeholder: '请选择单位',
  183. showSearch: true,
  184. virtual: true,
  185. optionFilterProp: 'label'
  186. },
  187. 'x-reactions': ['{{useAsyncDataSource(loadInstitutionList)}}'],
  188. 'x-index': 3
  189. },
  190. ID: {
  191. type: 'string',
  192. title: '',
  193. required: true,
  194. 'x-decorator': 'FormItem',
  195. 'x-component': 'Select',
  196. 'x-component-props': {
  197. placeholder: '请选择用户',
  198. showSearch: true,
  199. optionFilterProp: 'label'
  200. },
  201. 'x-index': 4
  202. }
  203. }
  204. }