CanvasNode.vue 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. <script setup lang="ts">
  2. import { computed } from 'vue'
  3. import { Position } from '@vue-flow/core'
  4. import type { NodeProps } from '@vue-flow/core'
  5. import type {
  6. IWorkflowNode,
  7. CanvasConnectionPort,
  8. CanvasElementPortWithRenderData
  9. } from '../../Interface'
  10. import { Icon } from '@repo/ui'
  11. import CanvasHandle from './handles/CanvasHandle.vue'
  12. type Props = NodeProps<IWorkflowNode['data']> & {
  13. readOnly?: boolean
  14. hovered?: boolean
  15. }
  16. const props = defineProps<Props>()
  17. /**
  18. * 处理节点
  19. */
  20. const createEndpoint = (data: {
  21. port: CanvasConnectionPort
  22. index: number
  23. count: number
  24. offsetAxis: 'top' | 'left'
  25. position: Position
  26. }): CanvasElementPortWithRenderData => {
  27. const { port, index, count, offsetAxis, position } = data
  28. return {
  29. ...port,
  30. handleId: `${port.type}-${index}`,
  31. position,
  32. connectionsCount: count,
  33. isConnecting: false,
  34. offset: {
  35. [offsetAxis]: `${(100 / (count + 1)) * (index + 1)}%`
  36. }
  37. }
  38. }
  39. /**
  40. * Inputs
  41. */
  42. const inputs = computed(() =>
  43. (props.data.inputs || []).map((target, index) =>
  44. createEndpoint({
  45. port: target,
  46. index,
  47. count: props.data.inputs.length,
  48. offsetAxis: 'top',
  49. position: Position.Left
  50. })
  51. )
  52. )
  53. /**
  54. * Outputs
  55. */
  56. const outputs = computed(() =>
  57. (props.data.outputs || []).map((source, index) =>
  58. createEndpoint({
  59. port: source,
  60. index,
  61. count: props.data.outputs.length,
  62. offsetAxis: 'top',
  63. position: Position.Right
  64. })
  65. )
  66. )
  67. const nodeClass = computed(() => {
  68. let classes: string[] = []
  69. if (props.selected) {
  70. classes.push('ring-6px', 'ring-#e0e2e7')
  71. }
  72. if (inputs.value.length === 0) {
  73. classes.push('rounded-l-36px')
  74. }
  75. return classes
  76. })
  77. </script>
  78. <template>
  79. <div
  80. class="w-full h-full bg-#fff box-border border-2 border-solid border-#dcdcdc rounded-8px relative"
  81. :class="nodeClass"
  82. >
  83. <div className="w-full h-full relative flex items-center justify-center">
  84. <Icon :icon="data?.icon" height="40" width="40" :color="data?.iconColor" />
  85. </div>
  86. <div className="absolute w-full bottom--24px text-12px text-center text-#222">
  87. {{ data?.displayName }}
  88. </div>
  89. <div className="absolute w-full bottom--40px text-12px text-center text-#999 truncate">
  90. {{ data.subtitle }}
  91. </div>
  92. <template v-for="target in inputs" :key="'handle-inputs-port' + target.index">
  93. <CanvasHandle v-bind="target" type="target" />
  94. </template>
  95. <template v-for="source in outputs" :key="'handle-outputs-port' + source.index">
  96. <CanvasHandle v-bind="source" type="source" />
  97. </template>
  98. </div>
  99. </template>