Editor.vue 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. <template>
  2. <div class="w-full h-full flex flex-col">
  3. <div
  4. class="h-60px shrink-0 border-b border-b-solid border-gray-200 flex items-center justify-between px-12px"
  5. >
  6. <div class="left flex items-center gap-4">
  7. <el-breadcrumb separator="/">
  8. <el-breadcrumb-item>Workspace</el-breadcrumb-item>
  9. <el-breadcrumb-item>workflow_1</el-breadcrumb-item>
  10. </el-breadcrumb>
  11. <IconButton icon="iconoir:plus" type="primary" link>标签</IconButton>
  12. </div>
  13. <div class="right flex items-center gap-2">
  14. <el-button type="default" size="small">发布</el-button>
  15. <IconButton icon="lucide:history" type="default" link></IconButton>
  16. <el-dropdown placement="bottom-end" popper-class="w-120px">
  17. <IconButton icon="fluent-mdl2:more" type="default" link></IconButton>
  18. <template #dropdown>
  19. <el-dropdown-item>描述</el-dropdown-item>
  20. <el-dropdown-item>复用</el-dropdown-item>
  21. <el-dropdown-item>重命名</el-dropdown-item>
  22. <el-dropdown-item divided>删除</el-dropdown-item>
  23. </template>
  24. </el-dropdown>
  25. </div>
  26. </div>
  27. <el-splitter layout="vertical" class="flex-1">
  28. <el-splitter-panel>
  29. <Workflow
  30. :workflow="workflow"
  31. @click:node="handleNodeClick"
  32. @create:node="handleNodeCreate"
  33. @create:connection="onCreateConnection"
  34. @drop="handleDrop"
  35. @run="handleRunWorkflow"
  36. @update:nodes:position="handleUpdateNodesPosition"
  37. @update:node:attrs="handleUpdateNodeProps"
  38. class="bg-#f5f5f5"
  39. />
  40. <RunWorkflow v-model:visible="runVisible" />
  41. <Setter
  42. :id="nodeID"
  43. :workflow="workflow"
  44. @update:node:data="hangleUpdateNodeData"
  45. v-model:visible="setterVisible"
  46. />
  47. </el-splitter-panel>
  48. <el-splitter-panel v-model:size.lazy="footerHeight" :min="32">
  49. <EditorFooter @toggle="handleFooterToggle" />
  50. </el-splitter-panel>
  51. </el-splitter>
  52. </div>
  53. </template>
  54. <script setup lang="ts">
  55. import { ref, inject, type CSSProperties, onBeforeUnmount } from 'vue'
  56. import { startNode, endNode, httpNode, conditionNode, databaseNode, codeNode } from '@repo/nodes'
  57. import { Workflow, type IWorkflow, type XYPosition, type Connection } from '@repo/workflow'
  58. import { v4 as uuid } from 'uuid'
  59. import Setter from '@/components/setter/index.vue'
  60. import RunWorkflow from '@/components/RunWorkflow/index.vue'
  61. import EditorFooter from '@/features/editorFooter/index.vue'
  62. import { IconButton } from '@repo/ui'
  63. import type { SourceType } from '@repo/nodes'
  64. const layout = inject<{ setMainStyle: (style: CSSProperties) => void }>('layout')
  65. layout?.setMainStyle({
  66. padding: '0px'
  67. })
  68. const footerHeight = ref(32)
  69. const workflow = ref<IWorkflow>({
  70. id: uuid(),
  71. nodes: [startNode, endNode],
  72. edges: []
  73. })
  74. /**
  75. * Editor
  76. */
  77. const handleFooterToggle = (open: boolean) => {
  78. footerHeight.value = open ? 200 : 32
  79. }
  80. /**
  81. * Workflow
  82. */
  83. const nodeID = ref('')
  84. const setterVisible = ref(false)
  85. const runVisible = ref(false)
  86. const handleRunWorkflow = () => {
  87. runVisible.value = true
  88. console.log('run workflow')
  89. }
  90. const handleNodeCreate = (value: SourceType | string) => {
  91. const id = uuid()
  92. if (typeof value === 'string') {
  93. if (value === 'stickyNote') {
  94. workflow.value.nodes.push({
  95. id,
  96. type: 'canvas-node',
  97. zIndex: -1,
  98. position: { x: 600, y: 300 },
  99. data: {
  100. id,
  101. version: ['1.0.0'],
  102. inputs: [],
  103. outputs: [],
  104. renderType: 'stickyNote',
  105. content: '注释内容,可以使用 **Markdown** 语法进行格式化, 双击进入编辑。',
  106. width: 400,
  107. height: 200,
  108. color: '#fff5d6'
  109. }
  110. })
  111. }
  112. return
  113. }
  114. const nodeMap: Record<string, any> = {
  115. start: startNode,
  116. end: endNode,
  117. http: httpNode,
  118. condition: conditionNode,
  119. code: codeNode,
  120. database: databaseNode
  121. }
  122. const nodeToAdd = nodeMap[value.type]
  123. // 如果存在对应节点则添加
  124. if (nodeToAdd) {
  125. workflow.value.nodes.push({
  126. ...nodeToAdd,
  127. data: {
  128. ...nodeToAdd.data,
  129. id
  130. },
  131. zIndex: 1,
  132. id: uuid()
  133. })
  134. }
  135. console.log(workflow.value.nodes, 'workflow.nodes')
  136. }
  137. const handleNodeClick = (id: string, position: XYPosition) => {
  138. nodeID.value = id
  139. setterVisible.value = true
  140. }
  141. const handleDrop = (position: XYPosition, event: DragEvent) => {
  142. console.log('drag and drop at', position, event)
  143. }
  144. /**
  145. * 创建连线
  146. */
  147. const onCreateConnection = (connection: Connection) => {
  148. const { source, target } = connection
  149. if (!workflow.value.edges.some((edge) => edge.source === source && edge.target === target)) {
  150. workflow.value.edges.push({
  151. id: `edge-${source}-${target}`,
  152. source,
  153. target,
  154. type: 'canvas-edge',
  155. data: {}
  156. })
  157. }
  158. }
  159. /**
  160. * 移动位置
  161. */
  162. const handleUpdateNodesPosition = (events: { id: string; position: XYPosition }[]) => {
  163. events?.forEach(({ id, position }) => {
  164. const node = workflow.value.nodes.find((node) => node.id === id)
  165. if (node) {
  166. node.position = position
  167. }
  168. })
  169. }
  170. /**
  171. * 修改节点数据
  172. */
  173. const hangleUpdateNodeData = (id: string, data: any) => {
  174. const node = workflow.value.nodes.find((node) => node.id === id)
  175. if (node) {
  176. node.data = {
  177. ...node.data,
  178. ...data
  179. }
  180. }
  181. console.log('hangleUpdateNodeData', id, data)
  182. }
  183. /**
  184. * 修改节点属性
  185. */
  186. const handleUpdateNodeProps = (id: string, attrs: Record<string, unknown>) => {
  187. const node = workflow.value.nodes.find((node) => node.id === id)
  188. if (node) {
  189. if (node.data?.renderType === 'stickyNote') {
  190. Object.assign(node.data, attrs)
  191. } else {
  192. Object.assign(node, attrs)
  193. }
  194. }
  195. }
  196. onBeforeUnmount(() => {
  197. layout?.setMainStyle({})
  198. })
  199. </script>