feat: 新增release页面和自定义多边形节点
新增release.vue页面,用于展示拖拽面板。在node-model中新增自定义多边形节点,并更新相关路由和依赖配置。
Showing
7 changed files
with
222 additions
and
15 deletions
This diff is collapsed. Click to expand it.
| ... | @@ -145,6 +145,11 @@ const router = createRouter({ | ... | @@ -145,6 +145,11 @@ const router = createRouter({ |
| 145 | name: 'api-transform-model', | 145 | name: 'api-transform-model', |
| 146 | component: () => import('../views/api/transformModel.vue') | 146 | component: () => import('../views/api/transformModel.vue') |
| 147 | }, | 147 | }, |
| 148 | + { | ||
| 149 | + path: '/release', | ||
| 150 | + name: 'release', | ||
| 151 | + component: () => import('../views/release.vue') | ||
| 152 | + }, | ||
| 148 | ] | 153 | ] |
| 149 | }) | 154 | }) |
| 150 | 155 | ... | ... |
| 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-19 00:57:55 | 4 | + * @LastEditTime: 2025-03-19 10:46:41 |
| 5 | * @FilePath: /logic-flow2/src/views/api/transformModel.vue | 5 | * @FilePath: /logic-flow2/src/views/api/transformModel.vue |
| 6 | * @Description: 拖拽面板 | 6 | * @Description: 拖拽面板 |
| 7 | --> | 7 | --> |
| ... | @@ -59,14 +59,17 @@ const centerView = () => { | ... | @@ -59,14 +59,17 @@ const centerView = () => { |
| 59 | if (nodes.length === 0) return; | 59 | if (nodes.length === 0) return; |
| 60 | 60 | ||
| 61 | // 计算所有节点的边界框 | 61 | // 计算所有节点的边界框 |
| 62 | - const bounds = nodes.reduce((acc, node) => { | 62 | + const bounds = nodes.reduce( |
| 63 | + (acc, node) => { | ||
| 63 | const { x, y } = node; | 64 | const { x, y } = node; |
| 64 | acc.minX = Math.min(acc.minX, x); | 65 | acc.minX = Math.min(acc.minX, x); |
| 65 | acc.maxX = Math.max(acc.maxX, x); | 66 | acc.maxX = Math.max(acc.maxX, x); |
| 66 | acc.minY = Math.min(acc.minY, y); | 67 | acc.minY = Math.min(acc.minY, y); |
| 67 | acc.maxY = Math.max(acc.maxY, y); | 68 | acc.maxY = Math.max(acc.maxY, y); |
| 68 | return acc; | 69 | return acc; |
| 69 | - }, { minX: Infinity, maxX: -Infinity, minY: Infinity, maxY: -Infinity }); | 70 | + }, |
| 71 | + { minX: Infinity, maxX: -Infinity, minY: Infinity, maxY: -Infinity } | ||
| 72 | + ); | ||
| 70 | 73 | ||
| 71 | // 计算中心点和范围 | 74 | // 计算中心点和范围 |
| 72 | const centerX = (bounds.minX + bounds.maxX) / 2; | 75 | const centerX = (bounds.minX + bounds.maxX) / 2; |
| ... | @@ -82,6 +85,7 @@ onMounted(() => { | ... | @@ -82,6 +85,7 @@ onMounted(() => { |
| 82 | lf = new LogicFlow({ | 85 | lf = new LogicFlow({ |
| 83 | container: container.value, | 86 | container: container.value, |
| 84 | grid: true, | 87 | grid: true, |
| 88 | + nodeTextDraggable: true, | ||
| 85 | }); | 89 | }); |
| 86 | 90 | ||
| 87 | // 监听点击画布功能 | 91 | // 监听点击画布功能 |
| ... | @@ -89,12 +93,11 @@ onMounted(() => { | ... | @@ -89,12 +93,11 @@ onMounted(() => { |
| 89 | console.log("Canvas clicked at:", e.x, e.y); | 93 | console.log("Canvas clicked at:", e.x, e.y); |
| 90 | }); | 94 | }); |
| 91 | 95 | ||
| 92 | - lf.on('node:click', (e) => { | 96 | + lf.on("node:click", (e) => { |
| 93 | console.log(e); | 97 | console.log(e); |
| 94 | // 示例:HTML坐标转换为画布坐标 | 98 | // 示例:HTML坐标转换为画布坐标 |
| 95 | console.warn(e); | 99 | console.warn(e); |
| 96 | 100 | ||
| 97 | - | ||
| 98 | const htmlPoint = { x: 100, y: 100 }; | 101 | const htmlPoint = { x: 100, y: 100 }; |
| 99 | const { transformModel } = lf.graphModel; | 102 | const { transformModel } = lf.graphModel; |
| 100 | const canvasPoint = transformModel.HtmlPointToCanvasPoint(htmlPoint); | 103 | const canvasPoint = transformModel.HtmlPointToCanvasPoint(htmlPoint); |
| ... | @@ -116,14 +119,17 @@ onMounted(() => { | ... | @@ -116,14 +119,17 @@ onMounted(() => { |
| 116 | const nodes = lf.graphModel.nodes; | 119 | const nodes = lf.graphModel.nodes; |
| 117 | if (nodes.length === 0) return; | 120 | if (nodes.length === 0) return; |
| 118 | 121 | ||
| 119 | - const bounds = nodes.reduce((acc, node) => { | 122 | + const bounds = nodes.reduce( |
| 123 | + (acc, node) => { | ||
| 120 | const { x, y } = node; | 124 | const { x, y } = node; |
| 121 | acc.minX = Math.min(acc.minX, x); | 125 | acc.minX = Math.min(acc.minX, x); |
| 122 | acc.maxX = Math.max(acc.maxX, x); | 126 | acc.maxX = Math.max(acc.maxX, x); |
| 123 | acc.minY = Math.min(acc.minY, y); | 127 | acc.minY = Math.min(acc.minY, y); |
| 124 | acc.maxY = Math.max(acc.maxY, y); | 128 | acc.maxY = Math.max(acc.maxY, y); |
| 125 | return acc; | 129 | return acc; |
| 126 | - }, { minX: Infinity, maxX: -Infinity, minY: Infinity, maxY: -Infinity }); | 130 | + }, |
| 131 | + { minX: Infinity, maxX: -Infinity, minY: Infinity, maxY: -Infinity } | ||
| 132 | + ); | ||
| 127 | 133 | ||
| 128 | const centerX = (bounds.minX + bounds.maxX) / 2; | 134 | const centerX = (bounds.minX + bounds.maxX) / 2; |
| 129 | const centerY = (bounds.minY + bounds.maxY) / 2; | 135 | const centerY = (bounds.minY + bounds.maxY) / 2; |
| ... | @@ -133,7 +139,10 @@ onMounted(() => { | ... | @@ -133,7 +139,10 @@ onMounted(() => { |
| 133 | // transformModel.focusOn(centerX, centerY, width, height); | 139 | // transformModel.focusOn(centerX, centerY, width, height); |
| 134 | }, 0); | 140 | }, 0); |
| 135 | 141 | ||
| 136 | - | 142 | + const { editConfigModel } = lf.graphModel; |
| 143 | + editConfigModel.updateEditConfig({ | ||
| 144 | + stopZoomGraph: true, | ||
| 145 | + }); | ||
| 137 | }); | 146 | }); |
| 138 | </script> | 147 | </script> |
| 139 | 148 | ... | ... |
src/views/node-model/customPolygon.js
0 → 100644
| 1 | +/* | ||
| 2 | + * @Date: 2025-03-19 17:24:14 | ||
| 3 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 4 | + * @LastEditTime: 2025-03-19 17:24:38 | ||
| 5 | + * @FilePath: /logic-flow2/src/views/node-model/customPolygon.js | ||
| 6 | + * @Description: 文件描述 | ||
| 7 | + */ | ||
| 8 | +import { PolygonNode, PolygonNodeModel } from '@logicflow/core'; | ||
| 9 | + | ||
| 10 | +class CustomPolygonModel extends PolygonNodeModel { | ||
| 11 | + // 默认四边形 => 八边形 | ||
| 12 | + setAttributes() { | ||
| 13 | + const width = 100; | ||
| 14 | + const height = 100; | ||
| 15 | + const x = 50; | ||
| 16 | + const y = 50; | ||
| 17 | + // 计算多边形的八个顶点, 中心点为[50, 50], 宽高均为100 | ||
| 18 | + const pointList = [ | ||
| 19 | + [x - 0.205 * width, y - 0.5 * height], | ||
| 20 | + [x + 0.205 * width, y - 0.5 * height], | ||
| 21 | + [x + 0.5 * width, y - 0.205 * height], | ||
| 22 | + [x + 0.5 * width, y + 0.205 * height], | ||
| 23 | + [x + 0.205 * width, y + 0.5 * height], | ||
| 24 | + [x - 0.205 * width, y + 0.5 * height], | ||
| 25 | + [x - 0.5 * width, y + 0.205 * height], | ||
| 26 | + [x - 0.5 * width, y - 0.205 * height], | ||
| 27 | + ]; | ||
| 28 | + this.points = pointList; | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + getTextStyle() { | ||
| 32 | + const { refX = 0, refY = 0 } = this.properties; | ||
| 33 | + const style = super.getTextStyle(); | ||
| 34 | + | ||
| 35 | + // 通过 transform 重新设置 text 的位置:向下移动70px | ||
| 36 | + return { | ||
| 37 | + ...style, | ||
| 38 | + transform: `matrix(1 0 0 1 ${refX} ${refY + 70})`, | ||
| 39 | + }; | ||
| 40 | + } | ||
| 41 | +} | ||
| 42 | + | ||
| 43 | +export default { | ||
| 44 | + type: 'custom-polygon', | ||
| 45 | + view: PolygonNode, | ||
| 46 | + model: CustomPolygonModel, | ||
| 47 | +}; |
| 1 | /* | 1 | /* |
| 2 | * @Date: 2025-03-11 15:09:49 | 2 | * @Date: 2025-03-11 15:09:49 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-03-11 15:45:06 | 4 | + * @LastEditTime: 2025-03-19 17:26:16 |
| 5 | - * @FilePath: /logic-flow2/src/views/node/data.js | 5 | + * @FilePath: /logic-flow2/src/views/node-model/data.js |
| 6 | * @Description: 文件描述 | 6 | * @Description: 文件描述 |
| 7 | */ | 7 | */ |
| 8 | export default { | 8 | export default { |
| ... | @@ -47,5 +47,12 @@ export default { | ... | @@ -47,5 +47,12 @@ export default { |
| 47 | height: 130, | 47 | height: 130, |
| 48 | }, | 48 | }, |
| 49 | }, | 49 | }, |
| 50 | + { | ||
| 51 | + id: '4', | ||
| 52 | + type: 'custom-polygon', | ||
| 53 | + x: 100, | ||
| 54 | + y: 300, | ||
| 55 | + text: 'custom-polygon', | ||
| 56 | + } | ||
| 50 | ], | 57 | ], |
| 51 | }; | 58 | }; | ... | ... |
| 1 | <!-- | 1 | <!-- |
| 2 | * @Date: 2025-03-11 15:07:29 | 2 | * @Date: 2025-03-11 15:07:29 |
| 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com | 3 | * @LastEditors: hookehuyr hookehuyr@gmail.com |
| 4 | - * @LastEditTime: 2025-03-11 16:18:37 | 4 | + * @LastEditTime: 2025-03-19 17:31:46 |
| 5 | - * @FilePath: /logic-flow2/src/views/node/index.vue | 5 | + * @FilePath: /logic-flow2/src/views/node-model/index.vue |
| 6 | * @Description: 自定义节点model | 6 | * @Description: 自定义节点model |
| 7 | --> | 7 | --> |
| 8 | <template> | 8 | <template> |
| ... | @@ -12,9 +12,10 @@ | ... | @@ -12,9 +12,10 @@ |
| 12 | </template> | 12 | </template> |
| 13 | 13 | ||
| 14 | <script setup> | 14 | <script setup> |
| 15 | -import LogicFlow from '@logicflow/core'; | 15 | +import LogicFlow from "@logicflow/core"; |
| 16 | -import UserTask from './customRect'; | 16 | +import UserTask from "./customRect"; |
| 17 | -import data from './data'; | 17 | +import customPolygon from "./customPolygon"; |
| 18 | +import data from "./data"; | ||
| 18 | 19 | ||
| 19 | const SilentConfig = { | 20 | const SilentConfig = { |
| 20 | isSilentMode: true, | 21 | isSilentMode: true, |
| ... | @@ -35,6 +36,15 @@ onMounted(() => { | ... | @@ -35,6 +36,15 @@ onMounted(() => { |
| 35 | }); | 36 | }); |
| 36 | 37 | ||
| 37 | lf.register(UserTask); | 38 | lf.register(UserTask); |
| 39 | + lf.register(customPolygon); | ||
| 40 | + | ||
| 41 | + lf.on("node:click", ({ data }) => { | ||
| 42 | + if (data.type === "custom-polygon") { | ||
| 43 | + const node = lf.getNodeModelById(data.id); | ||
| 44 | + console.log("节点顶点坐标:", node.points); | ||
| 45 | + } | ||
| 46 | + }); | ||
| 47 | + | ||
| 38 | lf.render(data); | 48 | lf.render(data); |
| 39 | lf.translateCenter(); | 49 | lf.translateCenter(); |
| 40 | }); | 50 | }); | ... | ... |
src/views/release.vue
0 → 100644
| 1 | +<!-- | ||
| 2 | + * @Date: 2025-03-10 16:52:35 | ||
| 3 | + * @LastEditors: hookehuyr hookehuyr@gmail.com | ||
| 4 | + * @LastEditTime: 2025-03-19 11:32:33 | ||
| 5 | + * @FilePath: /logic-flow2/src/views/release.vue | ||
| 6 | + * @Description: 拖拽面板 | ||
| 7 | +--> | ||
| 8 | +<template> | ||
| 9 | + <div class="container"> | ||
| 10 | + <div ref="container" class="flow-container"></div> | ||
| 11 | + </div> | ||
| 12 | +</template> | ||
| 13 | + | ||
| 14 | +<script setup> | ||
| 15 | +import LogicFlow from "@logicflow/core"; | ||
| 16 | + | ||
| 17 | +const container = ref(null); | ||
| 18 | +let lf = null; | ||
| 19 | + | ||
| 20 | +onMounted(() => { | ||
| 21 | + lf = new LogicFlow({ | ||
| 22 | + container: container.value, | ||
| 23 | + grid: true, | ||
| 24 | + nodeTextDraggable: true, | ||
| 25 | + nodeTextEdit: true, | ||
| 26 | + edgeTextEdit: true, | ||
| 27 | + stopScrollGraph: true, | ||
| 28 | + stopZoomGraph: false, | ||
| 29 | + style: { | ||
| 30 | + rect: { | ||
| 31 | + width: 100, | ||
| 32 | + height: 50, | ||
| 33 | + radius: 8, | ||
| 34 | + }, | ||
| 35 | + circle: { | ||
| 36 | + r: 30, | ||
| 37 | + }, | ||
| 38 | + }, | ||
| 39 | + }); | ||
| 40 | + | ||
| 41 | + lf.render({ | ||
| 42 | + nodes: [ | ||
| 43 | + { | ||
| 44 | + id: "start", | ||
| 45 | + type: "rect", | ||
| 46 | + x: 200, | ||
| 47 | + y: 100, | ||
| 48 | + text: "开始", | ||
| 49 | + properties: { | ||
| 50 | + nodeType: "start", | ||
| 51 | + style: { | ||
| 52 | + fill: "#e8f7ff", | ||
| 53 | + stroke: "#1890ff", | ||
| 54 | + strokeWidth: 2, | ||
| 55 | + }, | ||
| 56 | + textStyle: { | ||
| 57 | + color: "#1890ff", | ||
| 58 | + fontSize: 16, | ||
| 59 | + fontWeight: "bold", | ||
| 60 | + }, | ||
| 61 | + }, | ||
| 62 | + }, | ||
| 63 | + { | ||
| 64 | + id: "process", | ||
| 65 | + type: "circle", | ||
| 66 | + x: 400, | ||
| 67 | + y: 100, | ||
| 68 | + text: "处理", | ||
| 69 | + properties: { | ||
| 70 | + nodeType: "process", | ||
| 71 | + style: { | ||
| 72 | + fill: "#fff7e6", | ||
| 73 | + stroke: "#ffa940", | ||
| 74 | + strokeWidth: 2, | ||
| 75 | + }, | ||
| 76 | + }, | ||
| 77 | + }, | ||
| 78 | + ], | ||
| 79 | + edges: [ | ||
| 80 | + { | ||
| 81 | + id: "edge1", | ||
| 82 | + sourceNodeId: "start", | ||
| 83 | + targetNodeId: "process", | ||
| 84 | + type: "polyline", | ||
| 85 | + text: "流转", | ||
| 86 | + properties: { | ||
| 87 | + style: { | ||
| 88 | + stroke: "#1890ff", | ||
| 89 | + strokeWidth: 2, | ||
| 90 | + }, | ||
| 91 | + }, | ||
| 92 | + }, | ||
| 93 | + ], | ||
| 94 | + }); | ||
| 95 | + | ||
| 96 | + // 监听节点点击 | ||
| 97 | + lf.on("node:click", ({ data }) => { | ||
| 98 | + console.log("点击节点:", data); | ||
| 99 | + }); | ||
| 100 | + | ||
| 101 | + // 监听连线完成 | ||
| 102 | + lf.on("edge:connect", ({ data }) => { | ||
| 103 | + console.log("连线完成:", data); | ||
| 104 | + }); | ||
| 105 | + | ||
| 106 | + // 监听画布缩放 | ||
| 107 | + lf.on("graph:transform", (transform) => { | ||
| 108 | + console.log("画布变换:", transform); | ||
| 109 | + }); | ||
| 110 | + | ||
| 111 | + // 居中显示 | ||
| 112 | + lf.translateCenter(); | ||
| 113 | +}); | ||
| 114 | +</script> | ||
| 115 | + | ||
| 116 | +<style scoped> | ||
| 117 | +.container { | ||
| 118 | + width: 100vw; | ||
| 119 | + height: 100vh; | ||
| 120 | + display: flex; | ||
| 121 | + flex-direction: column; | ||
| 122 | +} | ||
| 123 | + | ||
| 124 | +.flow-container { | ||
| 125 | + flex: 1; | ||
| 126 | + width: 100%; | ||
| 127 | + height: 100%; | ||
| 128 | +} | ||
| 129 | +</style> |
-
Please register or login to post a comment