Browse Source

拖拽新增节点

CzRger 3 months ago
parent
commit
2d1c73fdc0

+ 1 - 0
package.json

@@ -11,6 +11,7 @@
   },
   "dependencies": {
     "@antv/x6": "^2.18.1",
+    "@antv/x6-plugin-dnd": "^2.1.1",
     "@antv/x6-plugin-history": "^2.2.4",
     "@antv/x6-plugin-minimap": "^2.0.7",
     "@antv/x6-plugin-snapline": "^2.1.7",

BIN
src/assets/images/answer.png


BIN
src/assets/images/if-else.png


BIN
src/assets/images/test.png


File diff suppressed because it is too large
+ 1 - 0
src/assets/svg/add.svg


+ 36 - 4
src/stores/modules/workflow.ts

@@ -1,4 +1,7 @@
 import {defineStore} from "pinia";
+import {NodeType, NodeTypeObj} from "@/views/workflow/types";
+import {handleNode} from "@/views/workflow/handle";
+import {getNodeDefault} from "@/views/workflow/config";
 
 export const useWorkflowStore = defineStore('workflow', {
   state: () => ({
@@ -7,16 +10,45 @@ export const useWorkflowStore = defineStore('workflow', {
       node: <any>null,
       show: false
     },
-    systemVars: [
-      {label: '查询内容', key: 'sys.query', type: 'String'},
-      {label: '用户ID', key: 'sys.user_id', type: 'String'},
-    ]
+    nodeSize: {
+
+    }
   }),
   getters: {
   },
   actions: {
     init(graph) {
       this.graph = graph
+      return new Promise(resolve => {
+        const keys = Object.keys(NodeTypeObj)
+        const arr: any = []
+        keys.forEach((v, i) => {
+          const d = handleNode(getNodeDefault(v as NodeType))
+          delete d.portMarkup
+          delete d.ports
+          const node = this.graph.createNode(d)
+          node.position(-99999, -99999)
+          this.graph.addNode(node)
+          arr.push({
+            node: node,
+            flag: false
+          })
+        })
+        let timer = setInterval(() => {
+          arr.forEach((v, i) => {
+            const s = v.node.size()
+            if (s.width > 1) {
+              this.nodeSize[keys[i]] = s
+              v.flag = true
+              this.graph.removeNode(v.node)
+            }
+          })
+          if (arr.every(v => v.flag)) {
+            clearInterval(timer)
+            resolve(null)
+          }
+        }, 100)
+      })
     },
     nodePanel(node) {
       this.panel.node = node

+ 55 - 3
src/views/workflow/chart/index.vue

@@ -97,6 +97,33 @@
             </div>
           </el-popover>
         </div>
+        <div>
+          <el-popover
+            :show-arrow="false"
+            :width="100"
+            :popper-style="{
+              padding: 0,
+            }">
+            <template #reference>
+              <div class="__hover-bg">
+                <SvgIcon name="add"/>
+              </div>
+            </template>
+            <div class="">
+              <div class="text-[18px] p-3 pb-1">新增节点</div>
+              <div class="px-1">
+                <div class="mt-2">
+                  <nodeAdd @onAddNode="onAddNode" ref="ref_dndContainer"/>
+                </div>
+                <div class="bg-[#eaecf0] w-full h-[1px] my-2"/>
+              </div>
+              <div class="text-[12px] text-[#676f83] p-3">
+                提示<br/>
+                点击后拖拽至指定位置后,松开鼠标即可放置到画布上。
+              </div>
+            </div>
+          </el-popover>
+        </div>
       </div>
     </div>
   </div>
@@ -117,6 +144,9 @@ import {GraphHistoryStep, MapGraphHistoryStep, NodeType} from "@/views/workflow/
 import { MiniMap } from '@antv/x6-plugin-minimap'
 import { History } from '@antv/x6-plugin-history'
 import {v4} from "uuid";
+import { Dnd } from '@antv/x6-plugin-dnd'
+import nodeAdd from './node-add.vue'
+import {delay} from "@/utils/czr-util";
 
 register({
   shape: 'workflow-node',
@@ -224,11 +254,14 @@ const state: any = reactive({
     canRedo: false,
     steps: [],
     current: 0
-  }
+  },
+  dnd: null,
+  dndNode: null
 })
 const ref_chart = ref()
 const ref_miniMap = ref()
-const initChart = () => {
+const ref_dndContainer = ref()
+const initChart = async () => {
   state.graph = new Graph({
     container: ref_chart.value,
     autoResize: true,
@@ -342,7 +375,7 @@ const initChart = () => {
       }
     }
   })
-  WorkflowStore.init(state.graph)
+  await WorkflowStore.init(state.graph)
   initPlug()
   initWatch()
   initNodes()
@@ -456,6 +489,12 @@ const initWatch = () => {
       if (options.name) {
         state.history.steps.unshift(options.name)
       }
+      if (options.name === GraphHistoryStep.Dnd) {
+        const nodeId = cmds[0].data.id
+        state.graph.getCellById(nodeId).data.workflowData.ports.forEach(p => {
+          WorkflowStore.layoutPort(nodeId, p.id)
+        })
+      }
     }
   })
 }
@@ -487,6 +526,14 @@ const initPlug = () => {
       }
     }),
   )
+  state.dnd = new Dnd({
+    target: state.graph,
+    getDragNode: (node: any) => {
+      node.size(WorkflowStore.nodeSize[node.data.workflowData.type])
+      return node
+    },
+    getDropNode: (node) => node.clone({ keepId: true }),
+  })
 }
 const setEdgeStyle = (edge) => edge.attr('line', edge.getAttrByPath('hover') || edge.getAttrByPath('select') ? lineActiveStyle : lineStyle)
 const graphZoom = (z) => {
@@ -518,6 +565,11 @@ const onHistoryClear = (first = '') => {
   state.history.steps = [first || GraphHistoryStep.Root]
   state.history.current = 0
 }
+const onAddNode = ({type, e}) => {
+  const node = state.graph.createNode(handleNode(getNodeDefault(type)))
+  console.log(node.id)
+  state.dnd.start(node, e)
+}
 watch(() => props.data, (n) => {
   if (n) {
     initChart()

+ 47 - 0
src/views/workflow/chart/node-add.vue

@@ -0,0 +1,47 @@
+<template>
+  <div class="node-add">
+    <div class="node-add-item __hover-bg" @click="$emit('onAddNode', {type: NodeType.Test, e: $event})">
+      <img src="@/assets/images/test.png"/>
+      {{ NodeTypeObj[NodeType.Test].title }}
+    </div>
+    <div class="node-add-item __hover-bg" @click="$emit('onAddNode', {type: NodeType.Answer, e: $event})">
+      <img src="@/assets/images/answer.png"/>
+      {{ NodeTypeObj[NodeType.Answer].title }}
+    </div>
+    <div class="node-add-item __hover-bg" @click="$emit('onAddNode', {type: NodeType.IfElse, e: $event})">
+      <img src="@/assets/images/if-else.png"/>
+      {{ NodeTypeObj[NodeType.IfElse].title }}
+    </div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import {getCurrentInstance, reactive, ref} from "vue";
+import {NodeType, NodeTypeObj} from "@/views/workflow/types";
+
+const emits = defineEmits(['onAddNode',]);
+const props = defineProps({
+})
+const {proxy}: any = getCurrentInstance()
+const state: any = reactive({})
+</script>
+
+<style lang="scss" scoped>
+.node-add {
+  display: flex;
+  flex-direction: column;
+  gap: 4px;
+  .node-add-item{
+    padding: 2px 4px;
+    font-size: 13px;
+    border-radius: 6px;
+    display: flex;
+    align-items: center;
+    >img {
+      width: 16px;
+      height: 16px;
+      margin: 4px;
+    }
+  }
+}
+</style>

+ 3 - 12
src/views/workflow/chart/node-port.vue

@@ -31,11 +31,7 @@
           </div>
         </div>
       </template>
-      <div class="node-add">
-        <div class="node-add-item __hover" @click="onAddNode(NodeType.Test)">{{ NodeTypeObj[NodeType.Test].title }}</div>
-        <div class="node-add-item __hover" @click="onAddNode(NodeType.Answer)">{{ NodeTypeObj[NodeType.Answer].title }}</div>
-        <div class="node-add-item __hover" @click="onAddNode(NodeType.IfElse)">{{ NodeTypeObj[NodeType.IfElse].title }}</div>
-      </div>
+      <nodeAdd @onAddNode="onAddNode" class="py-2 px-1"/>
     </ElPopover>
   </template>
 </template>
@@ -47,6 +43,7 @@ import {getNodeDefault} from "@/views/workflow/config";
 import {GraphHistoryStep, NodeType, NodeTypeObj} from "@/views/workflow/types";
 import {handleEdge, handleNode} from "@/views/workflow/handle";
 import SvgIcon from "@/components/SvgIcon/index.vue";
+import nodeAdd from './node-add.vue'
 
 const emits = defineEmits([])
 const props = defineProps({
@@ -58,7 +55,7 @@ const {proxy}: any = getCurrentInstance()
 const state: any = reactive({
   active: false
 })
-const onAddNode = (type) => {
+const onAddNode = ({type}) => {
   const node = getNodeDefault(type)
   const sons = props.graph.getSuccessors(props.node, {breadthFirst: true, deep: false, distance: 1}).filter(v => v.data.workflowData.edgeSource === props.port.id)
   const diff = 100
@@ -167,10 +164,4 @@ onMounted(() => {
   justify-content: center;
 }
 
-.node-add {
-  min-width: 100px;
-  padding: 6px 10px;
-  background-color: #ffffff;
-  font-size: 13px;
-}
 </style>

+ 5 - 3
src/views/workflow/handle.ts

@@ -1,10 +1,12 @@
 import {Markup} from "@antv/x6";
 import {lineStyle} from "@/views/workflow/config";
 import { v4 } from "uuid";
-import {useWorkflowStore} from "@/stores";
 import {GraphHistoryStep, NodeType} from "@/views/workflow/types";
 
-const WorkflowStore = useWorkflowStore()
+const systemVars = [
+  {label: '查询内容', key: 'sys.query', type: 'String'},
+  {label: '用户ID', key: 'sys.user_id', type: 'String'},
+]
 export const handleNode = (no) => {
   const id = v4()
   if (!no.id) {
@@ -78,7 +80,7 @@ export const handleNode = (no) => {
   }
   if (node.data.workflowData.type === NodeType.Root) {
     node.data.sysVars = [
-      ...WorkflowStore.systemVars
+      ...systemVars
     ]
   } else {
     node.ports.items.push({

+ 2 - 0
src/views/workflow/types.ts

@@ -113,12 +113,14 @@ export enum GraphHistoryStep {
   NodeDel = 'node-del',
   EdgeAdd = 'add-edge',
   EdgeDel = 'edge-del',
+  Dnd = 'dnd',
 }
 
 export const MapGraphHistoryStep = new Map([
   [GraphHistoryStep.Root, '会话开始'],
   [GraphHistoryStep.Move, '块已移动'],
   [GraphHistoryStep.NodeAdd, '块已添加'],
+  [GraphHistoryStep.Dnd, '块已添加'],
   [GraphHistoryStep.NodeDel, '块已删除'],
   [GraphHistoryStep.EdgeAdd, '块已连接'],
   [GraphHistoryStep.EdgeDel, '块已断开连接'],

+ 5 - 0
yarn.lock

@@ -23,6 +23,11 @@
   resolved "https://registry.npmmirror.com/@antv/x6-geometry/-/x6-geometry-2.0.5.tgz#c158317d74135bedd78c2fdeb76f9c7cfa0ef0aa"
   integrity sha512-MId6riEQkxphBpVeTcL4ZNXL4lScyvDEPLyIafvWMcWNTGK0jgkK7N20XSzqt8ltJb0mGUso5s56mrk8ysHu2A==
 
+"@antv/x6-plugin-dnd@^2.1.1":
+  version "2.1.1"
+  resolved "https://registry.npmmirror.com/@antv/x6-plugin-dnd/-/x6-plugin-dnd-2.1.1.tgz#4d9afed8e8105e01e31cdf526068fa686fb6b4aa"
+  integrity sha512-v0szzik1RkadPDn4Qi5mOSaB2AeI78D40/YuCYbPVzplG+HydGsHwO3MLTgJPQ+R5n0eM0W5F850p1VfTOHR7g==
+
 "@antv/x6-plugin-history@^2.2.4":
   version "2.2.4"
   resolved "https://registry.npmmirror.com/@antv/x6-plugin-history/-/x6-plugin-history-2.2.4.tgz#c4a543b8a2b6ae8b6934ec69067688459cae6c48"