CanvasNode.vue 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. <script setup lang="ts">
  2. import { computed, provide } from 'vue'
  3. import { Position } from '@vue-flow/core'
  4. import { nodeMap } from '@repo/nodes'
  5. import CanvasHandle from '../handles/CanvasHandle.vue'
  6. import NodeRenderer from './render-types/NodeRenderer.vue'
  7. import CanvasNodeToolBar from './CanvasNodeToolBar.vue'
  8. import type { NodeProps } from '@vue-flow/core'
  9. import type {
  10. IWorkflowNode,
  11. CanvasConnectionPort,
  12. CanvasElementPortWithRenderData
  13. } from '../../../Interface'
  14. type Props = NodeProps<IWorkflowNode['data']> & {
  15. readOnly?: boolean
  16. hovered?: boolean
  17. }
  18. const props = defineProps<Props>()
  19. const emit = defineEmits<{
  20. update: [id: string, parameters: Record<string, unknown>]
  21. move: [id: string, position: { x: number; y: number }]
  22. delete: [id: string]
  23. run: [id: string]
  24. }>()
  25. /**
  26. * 处理节点
  27. */
  28. const createEndpoint = (data: {
  29. port: CanvasConnectionPort
  30. index: number
  31. count: number
  32. offsetAxis: 'top' | 'left'
  33. position: Position
  34. type: 'source' | 'target'
  35. }): CanvasElementPortWithRenderData => {
  36. const { port, index, count, offsetAxis, position, type } = data
  37. console.log(port, 'port')
  38. return {
  39. ...port,
  40. handleId: port?.id || type,
  41. position,
  42. connectionsCount: count,
  43. isConnecting: false,
  44. offset: {
  45. [offsetAxis]: `${(100 / (count + 1)) * (index + 1)}%`
  46. }
  47. }
  48. }
  49. /**
  50. * Inputs
  51. */
  52. const inputs = computed(() => {
  53. const getInputs = nodeMap[props.data.nodeType]?.inputs
  54. const inputs = typeof getInputs === 'function' ? getInputs(props.data?.data) : getInputs || []
  55. return (inputs as CanvasConnectionPort[]).map((target, index) =>
  56. createEndpoint({
  57. port: target,
  58. index,
  59. count: inputs.length,
  60. offsetAxis: 'top',
  61. position: Position.Left,
  62. type: 'target'
  63. })
  64. )
  65. })
  66. /**
  67. * Outputs
  68. */
  69. const outputs = computed(() => {
  70. const getOutputs = nodeMap[props.data.nodeType]?.outputs
  71. const outputs = typeof getOutputs === 'function' ? getOutputs(props.data?.data) : getOutputs || []
  72. return (outputs as CanvasConnectionPort[]).map((target, index) =>
  73. createEndpoint({
  74. port: target,
  75. index,
  76. count: outputs.length,
  77. offsetAxis: 'top',
  78. position: Position.Right,
  79. type: 'source'
  80. })
  81. )
  82. })
  83. const onUpdate = (prop: Record<string, unknown>) => {
  84. emit('update', props.id, prop)
  85. }
  86. const onDelete = () => {
  87. emit('delete', props.id)
  88. }
  89. const onRun = () => {
  90. emit('run', props.id)
  91. }
  92. provide('canvas-node-data', {
  93. props,
  94. inputs,
  95. outputs
  96. })
  97. </script>
  98. <template>
  99. <div class="relative">
  100. <NodeRenderer v-bind="$attrs" @update="onUpdate" />
  101. <template v-for="target in inputs" :key="'handle-inputs-port' + target.index">
  102. <CanvasHandle v-bind="target" type="target" />
  103. </template>
  104. <template v-for="source in outputs" :key="'handle-outputs-port' + source.index">
  105. <CanvasHandle v-bind="source" type="source" />
  106. </template>
  107. <CanvasNodeToolBar @delete="onDelete" @run="onRun" />
  108. </div>
  109. </template>