Showing
8 changed files
with
197 additions
and
103 deletions
| 1 | /* | 1 | /* |
| 2 | - * @Date: 2025-03-10 13:15:30 | 2 | + * @Date: 2025-03-13 18:34:16 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-03-13 16:20:00 | 4 | + * @LastEditTime: 2025-03-16 00:21:53 |
| 5 | * @FilePath: /logic-flow2/src/router/index.js | 5 | * @FilePath: /logic-flow2/src/router/index.js |
| 6 | * @Description: 文件描述 | 6 | * @Description: 文件描述 |
| 7 | */ | 7 | */ |
| ... | @@ -120,6 +120,11 @@ const router = createRouter({ | ... | @@ -120,6 +120,11 @@ const router = createRouter({ |
| 120 | name: 'dynamic-group', | 120 | name: 'dynamic-group', |
| 121 | component: () => import('../views/dynamic-group/index.vue') | 121 | component: () => import('../views/dynamic-group/index.vue') |
| 122 | }, | 122 | }, |
| 123 | + { | ||
| 124 | + path: '/api-graphModel', | ||
| 125 | + name: 'api-graphModel', | ||
| 126 | + component: () => import('../views/api/graphModel.vue') | ||
| 127 | + }, | ||
| 123 | ] | 128 | ] |
| 124 | }) | 129 | }) |
| 125 | 130 | ... | ... |
| 1 | /* | 1 | /* |
| 2 | * @Date: 2025-03-13 16:11:47 | 2 | * @Date: 2025-03-13 16:11:47 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-03-13 17:09:32 | 4 | + * @LastEditTime: 2025-03-16 00:39:21 |
| 5 | * @FilePath: /logic-flow2/src/views/adv-menu/customNode.js | 5 | * @FilePath: /logic-flow2/src/views/adv-menu/customNode.js |
| 6 | * @Description: 文件描述 | 6 | * @Description: 文件描述 |
| 7 | */ | 7 | */ |
| ... | @@ -13,6 +13,15 @@ class CustomModel extends RectNodeModel { | ... | @@ -13,6 +13,15 @@ class CustomModel extends RectNodeModel { |
| 13 | const { | 13 | const { |
| 14 | properties: { isDisabledNode }, | 14 | properties: { isDisabledNode }, |
| 15 | } = this; | 15 | } = this; |
| 16 | + // 设置节点是否可以被连接 | ||
| 17 | + this.sourceRules.push({ | ||
| 18 | + message: "禁用节点不能作为连接源", | ||
| 19 | + validate: () => !isDisabledNode | ||
| 20 | + }); | ||
| 21 | + this.targetRules.push({ | ||
| 22 | + message: "禁用节点不能作为连接目标", | ||
| 23 | + validate: () => !isDisabledNode | ||
| 24 | + }); | ||
| 16 | if (!isDisabledNode) { | 25 | if (!isDisabledNode) { |
| 17 | // 单独为非禁用的元素设置菜单。 | 26 | // 单独为非禁用的元素设置菜单。 |
| 18 | this.menu = [ | 27 | this.menu = [ |
| ... | @@ -46,8 +55,15 @@ class CustomModel extends RectNodeModel { | ... | @@ -46,8 +55,15 @@ class CustomModel extends RectNodeModel { |
| 46 | // 添加 getNodeStyle 方法 | 55 | // 添加 getNodeStyle 方法 |
| 47 | getNodeStyle() { | 56 | getNodeStyle() { |
| 48 | const style = super.getNodeStyle(); | 57 | const style = super.getNodeStyle(); |
| 49 | - style.stroke = "#1E90FF"; | 58 | + const { isDisabledNode } = this.properties; |
| 50 | - style.fill = "#F0F8FF"; | 59 | + if (isDisabledNode) { |
| 60 | + style.stroke = "#999999"; | ||
| 61 | + style.fill = "#f0f0f0"; | ||
| 62 | + style.strokeDasharray = "3 3"; | ||
| 63 | + } else { | ||
| 64 | + style.stroke = "#1E90FF"; | ||
| 65 | + style.fill = "#F0F8FF"; | ||
| 66 | + } | ||
| 51 | return style; | 67 | return style; |
| 52 | } | 68 | } |
| 53 | } | 69 | } | ... | ... |
| 1 | <!-- | 1 | <!-- |
| 2 | * @Date: 2025-03-10 16:52:35 | 2 | * @Date: 2025-03-10 16:52:35 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-03-13 16:51:49 | 4 | + * @LastEditTime: 2025-03-16 00:45:35 |
| 5 | * @FilePath: /logic-flow2/src/views/adv-menu/index.vue | 5 | * @FilePath: /logic-flow2/src/views/adv-menu/index.vue |
| 6 | * @Description: 拖拽面板 | 6 | * @Description: 拖拽面板 |
| 7 | --> | 7 | --> |
| ... | @@ -48,7 +48,8 @@ onMounted(() => { | ... | @@ -48,7 +48,8 @@ onMounted(() => { |
| 48 | nodes: [ | 48 | nodes: [ |
| 49 | { id: "node1", type: "rect", x: 200, y: 100 }, | 49 | { id: "node1", type: "rect", x: 200, y: 100 }, |
| 50 | { id: "node2", type: "circle", x: 400, y: 100 }, | 50 | { id: "node2", type: "circle", x: 400, y: 100 }, |
| 51 | - { id: "node3", type: "custom-node", x: 600, y: 100 }, | 51 | + { id: "node3", type: "custom-node", x: 600, y: 100, properties: { isDisabledNode: true } }, |
| 52 | + { id: "node4", type: "custom-node", x: 600, y: 300, properties: { isDisabledNode: false } }, | ||
| 52 | ], | 53 | ], |
| 53 | edges: [{ id: "edge1", sourceNodeId: "node1", targetNodeId: "node2" }], | 54 | edges: [{ id: "edge1", sourceNodeId: "node1", targetNodeId: "node2" }], |
| 54 | }); | 55 | }); | ... | ... |
src/views/api/customNode.js
0 → 100644
| 1 | +import { RectNode, RectNodeModel } from "@logicflow/core"; | ||
| 2 | + | ||
| 3 | +class CustomNodeModel extends RectNodeModel { | ||
| 4 | + // 重写获取文本位置的方法 | ||
| 5 | + getTextStyle() { | ||
| 6 | + const style = super.getTextStyle(); | ||
| 7 | + style.textWidth = 200; // 设置更大的文本宽度 | ||
| 8 | + return style; | ||
| 9 | + } | ||
| 10 | +} | ||
| 11 | + | ||
| 12 | +class CustomNode extends RectNode { | ||
| 13 | + // 扩大文本点击区域 | ||
| 14 | + getTextBBox() { | ||
| 15 | + const { model } = this.props; | ||
| 16 | + const { x, y, width, height } = model; | ||
| 17 | + return { | ||
| 18 | + x: x - width / 2, | ||
| 19 | + y: y - height / 2, | ||
| 20 | + width: width, | ||
| 21 | + height: height, | ||
| 22 | + }; | ||
| 23 | + } | ||
| 24 | +} | ||
| 25 | + | ||
| 26 | +export default { | ||
| 27 | + type: "custom-rect", | ||
| 28 | + view: CustomNode, | ||
| 29 | + model: CustomNodeModel, | ||
| 30 | +}; |
src/views/api/graphModel.js
0 → 100644
File mode changed
src/views/api/graphModel.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <div class="container"> | ||
| 3 | + <div ref="container" class="flow-container"></div> | ||
| 4 | + </div> | ||
| 5 | +</template> | ||
| 6 | + | ||
| 7 | +<script setup> | ||
| 8 | +import LogicFlow from "@logicflow/core"; | ||
| 9 | +import { MiniMap, Control } from "@logicflow/extension"; | ||
| 10 | +import CustomNode from './customNode'; | ||
| 11 | +const container = ref(null); | ||
| 12 | +let lf = null; | ||
| 13 | + | ||
| 14 | +onMounted(() => { | ||
| 15 | + nextTick(() => { | ||
| 16 | + lf = new LogicFlow({ | ||
| 17 | + container: container.value, | ||
| 18 | + grid: true, | ||
| 19 | + plugins: [MiniMap, Control], | ||
| 20 | + width: container.value.offsetWidth, | ||
| 21 | + height: container.value.offsetHeight | ||
| 22 | + }); | ||
| 23 | + | ||
| 24 | + // 注册自定义节点 | ||
| 25 | + lf.register(CustomNode); | ||
| 26 | + | ||
| 27 | + // 监听节点点击事件,点击时进入编辑模式 | ||
| 28 | + lf.on("node:click", ({ data }) => { | ||
| 29 | + lf.graphModel.editText(data.id); | ||
| 30 | + }); | ||
| 31 | + | ||
| 32 | + // 监听文本编辑完成事件 | ||
| 33 | + lf.on("text:update", ({ data }) => { | ||
| 34 | + console.log("文本更新为:", data.text); | ||
| 35 | + }); | ||
| 36 | + | ||
| 37 | + // 渲染初始数据 | ||
| 38 | + lf.render({ | ||
| 39 | + nodes: [ | ||
| 40 | + { | ||
| 41 | + id: "node1", | ||
| 42 | + type: "rect", | ||
| 43 | + x: 100, | ||
| 44 | + y: 100, | ||
| 45 | + text: "点击编辑文本" | ||
| 46 | + }, | ||
| 47 | + { | ||
| 48 | + id: "node2", | ||
| 49 | + type: "custom-rect", // 使用自定义节点类型 | ||
| 50 | + x: 200, | ||
| 51 | + y: 200, | ||
| 52 | + text: "点击编辑文本" | ||
| 53 | + } | ||
| 54 | + ] | ||
| 55 | + }); | ||
| 56 | + }); | ||
| 57 | +}); | ||
| 58 | + | ||
| 59 | +// 判断节点是否可连接 | ||
| 60 | +const checkNodeConnectable = (targetNode, sourceNode) => { | ||
| 61 | + return targetNode.type === 'rect'; | ||
| 62 | +} | ||
| 63 | +</script> | ||
| 64 | + | ||
| 65 | +<style scoped> | ||
| 66 | +.container { | ||
| 67 | + width: 100vw; | ||
| 68 | + height: 100vh; | ||
| 69 | +} | ||
| 70 | +.flow-container { | ||
| 71 | + width: 100%; | ||
| 72 | + height: 100%; | ||
| 73 | + min-height: 500px; | ||
| 74 | +} | ||
| 75 | +</style> |
| 1 | <!-- | 1 | <!-- |
| 2 | * @Date: 2025-03-10 16:52:35 | 2 | * @Date: 2025-03-10 16:52:35 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-03-14 21:42:06 | 4 | + * @LastEditTime: 2025-03-15 19:24:15 |
| 5 | * @FilePath: /logic-flow2/src/views/control.vue | 5 | * @FilePath: /logic-flow2/src/views/control.vue |
| 6 | * @Description: 拖拽面板 | 6 | * @Description: 拖拽面板 |
| 7 | --> | 7 | --> |
| ... | @@ -18,107 +18,72 @@ const container = ref(null); | ... | @@ -18,107 +18,72 @@ const container = ref(null); |
| 18 | let lf = null; | 18 | let lf = null; |
| 19 | 19 | ||
| 20 | onMounted(() => { | 20 | onMounted(() => { |
| 21 | - lf = new LogicFlow({ | 21 | + nextTick(() => { // 使用 nextTick 确保 DOM 已挂载 |
| 22 | - container: container.value, | 22 | + lf = new LogicFlow({ |
| 23 | - grid: true, | 23 | + container: container.value, |
| 24 | - plugins: [MiniMap, Control], | 24 | + grid: true, |
| 25 | - }); | 25 | + plugins: [MiniMap, Control], |
| 26 | - | 26 | + }); |
| 27 | - lf.extension.control.addItem({ | ||
| 28 | - key: "mini-map", | ||
| 29 | - iconClass: "custom-minimap", | ||
| 30 | - title: "", | ||
| 31 | - text: "导航", | ||
| 32 | - onMouseEnter: (lf, ev) => { | ||
| 33 | - const position = lf.getPointByClient(ev.x, ev.y); | ||
| 34 | - lf.extension.miniMap.show( | ||
| 35 | - position.domOverlayPosition.x - 120, | ||
| 36 | - position.domOverlayPosition.y + 35 | ||
| 37 | - ); | ||
| 38 | - }, | ||
| 39 | - onClick: (lf, ev) => { | ||
| 40 | - const position = lf.getPointByClient(ev.x, ev.y); | ||
| 41 | - lf.extension.miniMap.show( | ||
| 42 | - position.domOverlayPosition.x - 120, | ||
| 43 | - position.domOverlayPosition.y + 35 | ||
| 44 | - ); | ||
| 45 | - }, | ||
| 46 | - }); | ||
| 47 | - | ||
| 48 | - // 添加克隆节点功能 | ||
| 49 | - lf.extension.control.addItem({ | ||
| 50 | - key: "clone-node", | ||
| 51 | - iconClass: "custom-clone", | ||
| 52 | - title: "克隆节点", | ||
| 53 | - text: "克隆", | ||
| 54 | - onClick: (lf, ev) => { | ||
| 55 | - const nodes = lf.getSelectElements().nodes; | ||
| 56 | - if (nodes.length) { | ||
| 57 | - nodes.forEach((node) => { | ||
| 58 | - const { x, y, type, properties } = node; | ||
| 59 | - // 在原节点右侧50px处创建新节点 | ||
| 60 | - lf.addNode({ | ||
| 61 | - type, | ||
| 62 | - x: x + 50, | ||
| 63 | - y, | ||
| 64 | - properties, | ||
| 65 | - }); | ||
| 66 | - }); | ||
| 67 | - } | ||
| 68 | - console.warn("克隆节点", lf.getGraphData()); | ||
| 69 | - }, | ||
| 70 | - }); | ||
| 71 | 27 | ||
| 28 | + // 监听锚点拖拽连线成功事件 | ||
| 29 | + lf.on("anchor:drop", ({ data, e }) => { | ||
| 30 | + console.log("手动创建连线成功:", { | ||
| 31 | + edgeId: data.id, | ||
| 32 | + sourceNode: data.sourceNodeId, | ||
| 33 | + targetNode: data.targetNodeId, | ||
| 34 | + edgeType: data.type | ||
| 35 | + }); | ||
| 36 | + }); | ||
| 72 | 37 | ||
| 73 | - // 添加修改节点ID功能 | 38 | + // 监听所有连线创建事件(包括手动和自动) |
| 74 | - lf.extension.control.addItem({ | 39 | + lf.on("edge:add", ({ data, e }) => { |
| 75 | - key: "change-node-id", | 40 | + console.log("连线创建事件:", { |
| 76 | - iconClass: "custom-edit", | 41 | + edgeId: data.id, |
| 77 | - title: "修改节点ID", | 42 | + sourceNode: data.sourceNodeId, |
| 78 | - text: "改ID", | 43 | + targetNode: data.targetNodeId, |
| 79 | - onClick: (lf, ev) => { | 44 | + edgeType: data.type, |
| 80 | - const nodes = lf.getSelectElements().nodes; | 45 | + isManual: e ? true : false, // render 时创建的边,e 为 undefined |
| 81 | - if (nodes.length === 1) { | 46 | + createType: e ? '手动创建' : 'render创建' |
| 82 | - const node = nodes[0]; | 47 | + }); |
| 83 | - const newId = `node_${Date.now()}`; // 生成新ID | 48 | + console.log("监听到连线创建:", data); |
| 84 | - lf.changeNodeId(node.id, newId); | 49 | + }); |
| 85 | - } else { | ||
| 86 | - alert('请选择一个节点'); | ||
| 87 | - } | ||
| 88 | - }, | ||
| 89 | - }); | ||
| 90 | 50 | ||
| 91 | - // 添加获取节点信息功能 | 51 | + // graphModel 的事件监听 - 更底层,返回原始数据 |
| 92 | - lf.extension.control.addItem({ | 52 | + const { eventCenter } = lf.graphModel; |
| 93 | - key: "get-node-info", | 53 | + eventCenter.on("node:click", (args) => { |
| 94 | - iconClass: "custom-info", | 54 | + console.log("graphModel node:click", { |
| 95 | - title: "获取节点信息", | 55 | + position: args.position, // 原始坐标 |
| 96 | - text: "节点信息", | 56 | + data: args.data, // 原始节点数据 |
| 97 | - onClick: (lf, ev) => { | 57 | + e: args.e // 原始事件对象 |
| 98 | - const nodes = lf.getSelectElements().nodes; | 58 | + }); |
| 99 | - if (nodes.length === 1) { | 59 | + }); |
| 100 | - const node = nodes[0]; | ||
| 101 | - // 获取节点 model | ||
| 102 | - const nodeModel = lf.getNodeModelById(node.id); | ||
| 103 | - // 获取节点数据 | ||
| 104 | - const nodeData = lf.getNodeDataById(node.id); | ||
| 105 | 60 | ||
| 106 | - console.log('节点Model:', nodeModel); | 61 | + // LogicFlow 实例的事件监听 - 更上层,返回处理后的数据 |
| 107 | - console.log('节点Data:', nodeData); | 62 | + lf.on("node:click", ({ data, e }) => { |
| 108 | - } else { | 63 | + console.log("lf node:click", { |
| 109 | - alert('请选择一个节点'); | 64 | + id: data.id, // 节点ID |
| 110 | - } | 65 | + type: data.type, // 节点类型 |
| 111 | - }, | 66 | + properties: data.properties, // 节点属性 |
| 67 | + x: data.x, // 处理后的坐标 | ||
| 68 | + y: data.y | ||
| 69 | + }); | ||
| 112 | }); | 70 | }); |
| 113 | 71 | ||
| 114 | - lf.extension.control.removeItem("mini-map"); | 72 | + lf.render({ |
| 73 | + nodes: [ | ||
| 74 | + { id: "node1", type: "rect", x: 200, y: 100 }, | ||
| 75 | + { id: "node2", type: "circle", x: 400, y: 100 }, | ||
| 76 | + ], | ||
| 77 | + // edges: [{ id: "edge1", sourceNodeId: "node1", targetNodeId: "node2", type: "polyline" }], | ||
| 78 | + }); | ||
| 115 | 79 | ||
| 116 | - lf.render({ | 80 | + // 然后手动添加边,这样会触发 edge:add 事件 |
| 117 | - nodes: [ | 81 | + lf.addEdge({ |
| 118 | - { id: "node1", type: "rect", x: 200, y: 100 }, | 82 | + id: "edge2", |
| 119 | - { id: "node2", type: "circle", x: 400, y: 100 }, | 83 | + sourceNodeId: "node1", |
| 120 | - ], | 84 | + targetNodeId: "node2", |
| 121 | - edges: [{ id: "edge1", sourceNodeId: "node1", targetNodeId: "node2" }], | 85 | + type: "polyline" |
| 86 | + }); | ||
| 122 | }); | 87 | }); |
| 123 | }); | 88 | }); |
| 124 | </script> | 89 | </script> |
| ... | @@ -135,5 +100,6 @@ onMounted(() => { | ... | @@ -135,5 +100,6 @@ onMounted(() => { |
| 135 | flex: 1; | 100 | flex: 1; |
| 136 | width: 100%; | 101 | width: 100%; |
| 137 | height: 100%; | 102 | height: 100%; |
| 103 | + min-height: 500px; /* 可添加最小高度作为保底 */ | ||
| 138 | } | 104 | } |
| 139 | </style> | 105 | </style> | ... | ... |
| 1 | <!-- | 1 | <!-- |
| 2 | * @Date: 2025-03-10 14:37:31 | 2 | * @Date: 2025-03-10 14:37:31 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-03-14 13:53:06 | 4 | + * @LastEditTime: 2025-03-16 00:22:53 |
| 5 | * @FilePath: /logic-flow2/src/views/home.vue | 5 | * @FilePath: /logic-flow2/src/views/home.vue |
| 6 | * @Description: 文件描述 | 6 | * @Description: 文件描述 |
| 7 | --> | 7 | --> |
| ... | @@ -28,6 +28,7 @@ | ... | @@ -28,6 +28,7 @@ |
| 28 | <el-button type="primary" @click="goTo('selection-select')">selection-select</el-button> | 28 | <el-button type="primary" @click="goTo('selection-select')">selection-select</el-button> |
| 29 | <el-button type="primary" @click="goTo('snapshot')">snapshot</el-button> | 29 | <el-button type="primary" @click="goTo('snapshot')">snapshot</el-button> |
| 30 | <el-button type="primary" @click="goTo('dynamic-group')">dynamic-group</el-button> | 30 | <el-button type="primary" @click="goTo('dynamic-group')">dynamic-group</el-button> |
| 31 | + <el-button type="primary" @click="goTo('api-graphModel')">api-graphModel</el-button> | ||
| 31 | </template> | 32 | </template> |
| 32 | 33 | ||
| 33 | <script setup> | 34 | <script setup> | ... | ... |
-
Please register or login to post a comment