CzRger hai 2 meses
pai
achega
267b7d1e6a

+ 1 - 0
package.json

@@ -11,6 +11,7 @@
   },
   "dependencies": {
     "@antv/x6": "^2.18.1",
+    "@antv/x6-plugin-snapline": "^2.1.7",
     "@antv/x6-vue-shape": "^2.1.2",
     "@types/node": "^20.17.11",
     "axios": "^1.7.9",

+ 71 - 0
src/views/workflow/chart/context-menu-tool.tsx

@@ -0,0 +1,71 @@
+import {ToolsView} from "@antv/x6";
+import {createApp} from "vue";
+import contextMenuTool from './context-menu-tool.vue'
+
+let app: any = null
+let timer: any = null;
+const ID = 'graph-dropdown'
+if (!document.getElementById(ID)) {
+  const dom = document.createElement('div')
+  dom.id = ID
+  document.body.appendChild(dom)
+}
+class ContextMenuTool extends ToolsView.ToolItem {
+  toggleContextMenu(visible, pos = {x: 0, y: 0}) {
+    const dom = document.getElementById(ID)
+    if (app) {
+      // 清空上次内容
+      app.unmount();
+      if (dom) {
+        dom.innerHTML = '';
+      }
+      app = null
+    }
+    document.removeEventListener('mousedown', this.onMouseDown)
+
+    if (visible && pos) {
+      console.log(this.options)
+      app = createApp(contextMenuTool, {data: this.options.data})
+      // 减去本身元素的宽高
+      if (dom) {
+        dom.style = `left: ${pos.x}px;top: ${pos.y}px;position: absolute;z-index: 100;`
+      }
+      app.mount(`#${ID}`)
+      document.addEventListener('mousedown', this.onMouseDown)
+    }
+  }
+
+
+  onMouseDown = () => {
+    timer = window.setTimeout(() => {
+      this.toggleContextMenu(false)
+    },200)
+  }
+
+  onContextMenu({ e }) {
+    if (timer) {
+      clearTimeout(timer)
+      timer = 0
+    }
+    this.toggleContextMenu(true, { x: e.pageX, y: e.pageY })
+  }
+
+  delegateEvents() {
+    this.cellView.on('cell:contextmenu', this.onContextMenu, this)
+    return super.delegateEvents()
+  }
+
+  onRemove() {
+    this.cellView.off('cell:contextmenu', this.onContextMenu, this)
+  }
+}
+
+ContextMenuTool.config({
+  tagName: 'div',
+  isSVGElement: false,
+  data: {},
+})
+
+export {
+  ContextMenuTool
+}

+ 28 - 0
src/views/workflow/chart/context-menu-tool.vue

@@ -0,0 +1,28 @@
+<template>
+  <div class="context-menu-tool">
+    右键菜单
+    <div>{{data}}</div>
+  </div>
+</template>
+
+<script setup lang="ts">
+import {getCurrentInstance, reactive, ref} from "vue";
+
+const emits = defineEmits([])
+const props = defineProps({
+  data: {}
+})
+const {proxy}: any = getCurrentInstance()
+const state: any = reactive({
+})
+</script>
+
+<style lang="scss" scoped>
+.context-menu-tool {
+  padding: 10px;
+  background-color: #ffffff;
+  border: 1px solid rgba(0, 0, 0, 0.1);
+  border-radius: 10px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+}
+</style>

+ 11 - 2
src/views/workflow/chart/index.vue

@@ -10,12 +10,13 @@
 import {createVNode, getCurrentInstance, inject, nextTick, onMounted, provide, reactive, ref, render, watch} from "vue";
 import {Graph, Markup, Shape} from '@antv/x6'
 import {WorkflowFunc} from "@/views/workflow/types";
-import {lineStyle, portStyle} from "@/views/workflow/config";
+import {lineStyle} from "@/views/workflow/config";
 import nodeAdd from './node-add.vue'
 import {register} from "@antv/x6-vue-shape";
 import WorkflowNode from "@/views/workflow/nodes/index.vue";
-import {merge} from "lodash";
 import {handleEdge, handleNode} from "@/views/workflow/handle";
+import { Snapline } from '@antv/x6-plugin-snapline'
+import {ContextMenuTool} from "./context-menu-tool";
 
 const workflowFuncInject = inject('workflowFunc', {} as WorkflowFunc)
 register({
@@ -68,6 +69,8 @@ Graph.registerPortLayout('more', (portsPositionArgs, elemBBox) => {
     }
   })
 })
+Graph.registerNodeTool('contextmenu', ContextMenuTool, true)
+Graph.registerEdgeTool('contextmenu', ContextMenuTool, true)
 const emits = defineEmits(['init'])
 const props = defineProps({
   data: <any>{}
@@ -145,6 +148,12 @@ const initChart = () => {
   initNodes()
   state.graph.zoomToFit({ maxScale: 1 })
   state.graph.centerContent() // 居中显示
+  state.graph.use(
+    new Snapline({
+      enabled: true,
+      clean: false,
+    }),
+  )
   initWatch()
   emits('init', state.graph)
 }

+ 16 - 1
src/views/workflow/handle.ts

@@ -1,4 +1,3 @@
-import { v4 } from "uuid";
 import {Markup} from "@antv/x6";
 import {merge} from "lodash";
 import {lineStyle, portStyle} from "@/views/workflow/config";
@@ -16,6 +15,14 @@ export const handleNode = (data) => {
       },
       items: []
     },
+    tools: [
+      {
+        name: 'contextmenu',
+        args: {
+          data
+        }
+      }
+    ]
   }
   node.data.id = data.id
   if (data.data.type !== 'root') {
@@ -66,6 +73,14 @@ export const handleEdge = (data) => {
         endDirections: ['left'],
       },
     },
+    tools: [
+      {
+        name: 'contextmenu',
+        args: {
+          data
+        }
+      }
+    ]
   }
   edge.source = {
     cell: data.source,

+ 5 - 0
yarn.lock

@@ -15,6 +15,11 @@
   resolved "https://registry.npmmirror.com/@antv/x6-geometry/-/x6-geometry-2.0.5.tgz#c158317d74135bedd78c2fdeb76f9c7cfa0ef0aa"
   integrity sha512-MId6riEQkxphBpVeTcL4ZNXL4lScyvDEPLyIafvWMcWNTGK0jgkK7N20XSzqt8ltJb0mGUso5s56mrk8ysHu2A==
 
+"@antv/x6-plugin-snapline@^2.1.7":
+  version "2.1.7"
+  resolved "https://registry.npmmirror.com/@antv/x6-plugin-snapline/-/x6-plugin-snapline-2.1.7.tgz#1b4e3be0a281ba324117b7ac7b9b8c39e484cce2"
+  integrity sha512-AsysoCb9vES0U2USNhEpYuO/W8I0aYfkhlbee5Kt4NYiMfQfZKQyqW/YjDVaS2pm38C1NKu1LdPVk/BBr4CasA==
+
 "@antv/x6-vue-shape@^2.1.2":
   version "2.1.2"
   resolved "https://registry.npmmirror.com/@antv/x6-vue-shape/-/x6-vue-shape-2.1.2.tgz#880abc2842f47f668c3de97cbacd1618bee1427a"