hookehuyr

自定义边操作

/*
* @Date: 2025-03-10 13:15:30
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-03-11 17:45:38
* @LastEditTime: 2025-03-12 11:06:26
* @FilePath: /logic-flow2/src/router/index.js
* @Description: 文件描述
*/
......@@ -44,7 +44,27 @@ const router = createRouter({
path: '/node-vue',
name: 'node-vue',
component: () => import('../views/node-vue/index.vue')
}
},
{
path: '/edge',
name: 'edge',
component: () => import('../views/edge/index.vue')
},
{
path: '/edge-custom',
name: 'edge-custom',
component: () => import('../views/edge/custom.vue')
},
{
path: '/edge-text',
name: 'edge-text',
component: () => import('../views/edge/text.vue')
},
{
path: '/edge-arrow',
name: 'edge-arrow',
component: () => import('../views/edge/arrow.vue')
},
]
})
......
<!--
* @Date: 2025-03-10 16:52:35
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-03-12 16:16:06
* @FilePath: /logic-flow2/src/views/edge/arrow.vue
* @Description: 自定义箭头
-->
<template>
<div class="container">
<div ref="container" class="flow-container"></div>
</div>
</template>
<script setup>
import LogicFlow from "@logicflow/core";
import CustomArrow from "./customArrow";
import data from "./data4";
const container = ref(null);
let lf = null;
onMounted(() => {
lf = new LogicFlow({
container: container.value,
grid: true,
edgeType: 'custom-arrow',
adjustEdge: true, // 开启边的调整功能
adjustEdgeStartAndEnd: true, // 允许调整边的起终点
});
lf.register(CustomArrow);
lf.render(data);
lf.translateCenter();
});
</script>
<style scoped>
.container {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
}
.flow-container {
flex: 1;
width: 100%;
height: 100%;
}
</style>
<!--
* @Date: 2025-03-10 16:52:35
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-03-12 14:13:59
* @FilePath: /logic-flow2/src/views/edge/custom.vue
* @Description: 自定义边
-->
<template>
<div class="container">
<div ref="container" class="flow-container"></div>
</div>
</template>
<script setup>
import LogicFlow from "@logicflow/core";
import sequence from "./sequence";
import data from "./data2";
const container = ref(null);
let lf = null;
onMounted(() => {
lf = new LogicFlow({
container: container.value,
grid: true,
});
lf.register(sequence);
// 设置当节点直接由用户手动连接的默认边类型
lf.setDefaultEdgeType("sequence");
lf.render(data);
lf.translateCenter();
lf.on("edge:click", ({ data }) => {
lf.getEdgeModelById(data.id).setText({
// draggable: true,
});
});
});
</script>
<style scoped>
.container {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
}
.flow-container {
flex: 1;
width: 100%;
height: 100%;
}
</style>
/*
* @Date: 2025-03-12 15:30:18
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-03-12 16:19:06
* @FilePath: /logic-flow2/src/views/edge/customArrow.js
* @Description: 文件描述
*/
import { PolylineEdge, PolylineEdgeModel, h } from '@logicflow/core';
class CustomEdgeModel extends PolylineEdgeModel {
// customTextPosition = true;
initEdgeData(data) {
super.initEdgeData(data);
this.customTextPosition = true;
this.adjustEdgeMiddle = true; // 允许调整边中间的点
}
setAttributes() {
this.isAnimation = true;
}
// setHovered(isHovered) {
// super.setHovered(isHovered);
// this.isAnimation = isHovered;
// }
getEdgeAnimationStyle() {
const style = super.getEdgeAnimationStyle();
style.strokeDasharray = "5 5";
style.strokeDashoffset = "100%";
style.animationDuration = "10s";
return style;
}
}
class CustomArrow extends PolylineEdge {
getAdjustPointShape(x, y) {
return h("g", {}, [
h("image", {
x: x - 9,
y: y - 9,
width: 18,
height: 18,
cursor: "move",
href:
"data:image/svg+xml;base64,PCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj48c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHdpZHRoPSIyMnB4IiBoZWlnaHQ9IjIycHgiIHZlcnNpb249IjEuMSI+PGNpcmNsZSBjeD0iMTEiIGN5PSIxMSIgcj0iNyIgc3Ryb2tlPSIjZmZmIiBmaWxsPSIjMjliNmYyIi8+PGNpcmNsZSBjeD0iMTEiIGN5PSIxMSIgcj0iMyIgc3Ryb2tlPSIjZmZmIiBmaWxsPSJ0cmFuc3BhcmVudCIvPjwvc3ZnPg=="
})
]);
}
getEndArrow() {
const { model } = this.props;
const {
properties: { arrowType },
} = model;
const { stroke, strokeWidth } = model.getArrowStyle();
const pathAttr = {
stroke,
strokeWidth,
};
if (arrowType === 'empty') {
// 空心箭头
return h('path', {
...pathAttr,
fill: '#FFF',
d: 'M 0 0 -20 -5 -30 0 -20 5 z',
});
} else if (arrowType === 'half') {
// 半箭头
return h('path', {
...pathAttr,
d: 'M 0 0 -10 5',
});
}
return h('path', {
...pathAttr,
d: 'M 0 0 -10 -5 -10 5 z',
});
}
}
export default {
type: 'custom-arrow',
model: CustomEdgeModel,
view: CustomArrow,
};
/*
* @Date: 2025-03-12 14:33:32
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-03-12 16:04:26
* @FilePath: /logic-flow2/src/views/edge/customEdge.js
* @Description: 文件描述
*/
/*
* @Date: 2025-03-12 14:33:32
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-03-12 15:14:53
* @FilePath: /logic-flow2/src/views/edge/customEdge.js
* @Description: 文件描述
*/
import { PolylineEdge, PolylineEdgeModel } from '@logicflow/core'
class CustomEdgeModel extends PolylineEdgeModel {
customTextPosition = true
getTextStyle() {
const style = super.getTextStyle()
// const { x: x1 } = this.pointsList[0];
// const { x: x2 } = this.pointsList[1];
// if (x1 === x2) {
// // 垂直
// style.textWidth = 20;
// } else {
// style.textWidth = 200;
// }
style.className = 'custom-text'
return style
}
getTextPosition() {
const position = super.getTextPosition()
const currentPositionList = this.points.split(' ') // 点位信息是以空格分隔的字符串, 例如点位可能是这样的格式:"100,200 150,200 150,300"
// const pointsList = []
// currentPositionList &&
// currentPositionList.forEach((item) => {
// const [x, y] = item.split(',') // 每个点的x,y坐标是以逗号分隔的
// pointsList.push({ x: Number(x), y: Number(y) })
// })
if (currentPositionList.length > 1) { // 确保至少有两个点
const [x1, y1] = currentPositionList[0].split(',') // 第一个点的坐标
const [x2, y2] = currentPositionList[1].split(',') // 第二个点的坐标
let distance = 50 // 默认偏移距离
if (Number(x1) === Number(x2)) { // 如果是垂直线
if (Number(y2) < Number(y1)) { // 如果是向上的线
distance = -50 // 偏移方向改为向上
}
position.y = Number(y1) + distance // 在y方向上偏移
position.x = Number(x1) // x保持不变
} else { // 如果是水平线或斜线
if (Number(x2) < Number(x1)) { // 如果是向左的线
distance = -50 // 偏移方向改为向左
}
position.x = Number(x1) + distance // 在x方向上偏移
position.y = Number(y1) - 10 // y方向略微上移
}
}
return position
}
}
class CustomEdge extends PolylineEdge {}
export default {
type: 'custom-edge',
model: CustomEdgeModel,
view: CustomEdge,
}
/*
* @Date: 2025-03-12 11:04:21
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-03-12 11:04:43
* @FilePath: /logic-flow2/src/views/edge/data.js
* @Description: 文件描述
*/
const data = {
nodes: [
{
id: '1',
type: 'rect',
x: 100,
y: 100,
text: '矩形1',
},
{
id: '2',
type: 'ellipse',
x: 500,
y: 100,
text: '椭圆2',
},
{
id: '3',
type: 'polygon',
x: 100,
y: 250,
text: '多边形3',
},
{
id: '4',
type: 'diamond',
x: 300,
y: 250,
text: '菱形4',
},
],
edges: [
{
sourceNodeId: '1',
targetNodeId: '2',
startPoint: {
// 起始点
x: 100,
y: 60,
},
endPoint: {
// 结束点
x: 500,
y: 50,
},
type: 'polyline',
text: 'polyline',
},
{
sourceNodeId: '2',
targetNodeId: '3',
type: 'line',
text: 'line',
},
{
sourceNodeId: '2',
targetNodeId: '4',
type: 'bezier',
text: 'bezier',
},
],
};
export default data;
/*
* @Date: 2025-03-12 12:02:21
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-03-12 12:02:27
* @FilePath: /logic-flow2/src/views/edge/data2.js
* @Description: 文件描述
*/
const data = {
nodes: [
{
id: '1',
type: 'rect',
x: 100,
y: 100,
text: '矩形',
},
{
id: '2',
type: 'circle',
x: 300,
y: 100,
text: '圆形',
},
{
id: '3',
type: 'ellipse',
x: 500,
y: 100,
text: '椭圆',
},
{
id: '4',
type: 'polygon',
x: 100,
y: 250,
text: '多边形',
},
{
id: '5',
type: 'diamond',
x: 300,
y: 250,
text: '菱形',
},
{
id: '6',
type: 'text',
x: 500,
y: 250,
text: '纯文本节点',
},
],
edges: [
{
id: '10',
sourceNodeId: '1',
targetNodeId: '3',
startPoint: {
x: 100,
y: 60,
},
endPoint: {
x: 500,
y: 50,
},
text: 'sequence',
type: 'sequence',
properties: {
isStrokeDashed: true, // 是否虚线
},
},
{
sourceNodeId: '3',
targetNodeId: '4',
type: 'line',
},
{
sourceNodeId: '3',
targetNodeId: '5',
type: 'line',
},
],
};
export default data;
/*
* @Date: 2025-03-12 14:36:57
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-03-12 15:22:29
* @FilePath: /logic-flow2/src/views/edge/data3.js
* @Description: 文件描述
*/
const data = {
nodes: [
{
id: 'rect_1',
type: 'rect',
x: 150,
y: 100,
text: 'rect',
},
{
id: 'circle_1',
type: 'circle',
x: 450,
y: 300,
text: 'circle',
},
{
id: '3',
type: 'ellipse',
x: 500,
y: 100,
text: '椭圆',
},
{
id: '4',
type: 'polygon',
x: 100,
y: 250,
text: '多边形',
},
{
id: '5',
type: 'diamond',
x: 300,
y: 250,
text: '菱形',
},
],
edges: [
{
sourceNodeId: 'rect_1',
targetNodeId: 'circle_1',
type: 'custom-edge',
text: '连线文本',
},
],
};
export default data;
/*
* @Date: 2025-03-12 15:30:01
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-03-12 15:30:10
* @FilePath: /logic-flow2/src/views/edge/data4.js
* @Description: 文件描述
*/
export default {
nodes: [
{
id: 'rect1',
type: 'rect',
x: 100,
y: 100,
text: 'rect1',
},
{
id: 'rect2',
type: 'rect',
x: 500,
y: 100,
text: 'rect2',
},
{
id: 'rect3',
type: 'rect',
x: 100,
y: 300,
text: 'rect3',
},
{
id: 'rect4',
type: 'rect',
x: 500,
y: 300,
text: 'rect4',
},
],
edges: [
{
id: 'customArrow1',
type: 'custom-arrow',
sourceNodeId: 'rect1',
targetNodeId: 'rect2',
properties: {
arrowType: 'empty',
},
text: '空心箭头',
},
{
id: 'customArrow2',
type: 'custom-arrow',
sourceNodeId: 'rect3',
targetNodeId: 'rect4',
properties: {
arrowType: 'half',
},
text: '半箭头',
},
],
};
.helloworld-app {
width: 100%;
.app-content {
height: 380px;
}
}
.viewport {
position: relative;
height: 80vh;
overflow: hidden;
}
<!--
* @Date: 2025-03-10 16:52:35
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-03-12 11:05:27
* @FilePath: /logic-flow2/src/views/edge/index.vue
* @Description: 拖拽面板
-->
<template>
<div class="container">
<div ref="container" class="flow-container"></div>
</div>
</template>
<script setup>
import LogicFlow from '@logicflow/core';
import data from './data';
import './index.less';
const SilentConfig = {
stopScrollGraph: true,
stopMoveGraph: true,
stopZoomGraph: true,
};
const container = ref(null);
let lf = null;
onMounted(() => {
lf = new LogicFlow({
container: container.value,
grid: true,
});
lf.render(data);
lf.translateCenter();
});
</script>
<style scoped>
.container {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
}
.flow-container {
flex: 1;
width: 100%;
height: 100%;
}
</style>
/*
* @Date: 2025-03-12 11:59:15
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-03-12 12:01:40
* @FilePath: /logic-flow2/src/views/edge/sequence.js
* @Description: 文件描述
*/
import { PolylineEdge, PolylineEdgeModel } from '@logicflow/core'
class SequenceModel extends PolylineEdgeModel {
// 设置边样式
getEdgeStyle() {
const style = super.getEdgeStyle()
const { properties } = this
if (properties.isStrokeDashed) {
style.strokeDasharray = '4, 4'
}
style.stroke = 'orange'
return style
}
// 设置边文本样式
getTextStyle() {
const style = super.getTextStyle()
style.color = '#3451F1'
style.fontSize = 20
style.background = Object.assign({}, style.background, {
fill: '#F2F131',
})
return style
}
// 设置 hover 轮廓样式
getOutlineStyle() {
const style = super.getOutlineStyle()
style.stroke = 'blue'
return style
}
}
export default {
type: 'sequence',
view: PolylineEdge,
model: SequenceModel,
}
<!--
* @Date: 2025-03-10 16:52:35
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-03-12 15:25:22
* @FilePath: /logic-flow2/src/views/edge/text.vue
* @Description: 自定义边文本位置
-->
<template>
<div class="container">
<div ref="container" class="flow-container"></div>
</div>
</template>
<script setup>
import LogicFlow from "@logicflow/core";
import customEdge from "./customEdge";
import data from "./data3";
const SilentConfig = {
stopScrollGraph: true,
stopMoveGraph: true,
stopZoomGraph: true,
};
const container = ref(null);
let lf = null;
onMounted(() => {
lf = new LogicFlow({
container: container.value,
grid: true,
...SilentConfig,
// 手动设置默认边
edgeType: "bezier",
// 移动已有边时会有 currentEdge 信息, 否则为空
edgeGenerator: (sourceNode, targetNode, currentEdge) => {
// 起始节点类型 rect 时使用 自定义的边 custom-edge
if (sourceNode.type === "rect") return "custom-edge";
},
});
lf.register(customEdge);
lf.setDefaultEdgeType("custom-edge");
lf.setTheme({
edgeText: {
textWidth: 100,
overflowMode: "autoWrap",
fontSize: 14,
background: {
fill: "red",
},
},
arrow: {
offset: 4, // 箭头垂线长度
verticalLength: 2, // 箭头底线长度
},
});
lf.render(data);
lf.translateCenter();
});
</script>
<style>
.container {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
}
.flow-container {
flex: 1;
width: 100%;
height: 100%;
}
:deep(.custom-text) {
font-size: 14px !important;
fill: #333 !important; /* 使用 fill 替代 color */
background-color: #fff !important;
padding: 4px 8px !important;
border-radius: 2px !important;
}
</style>
<!--
* @Date: 2025-03-10 14:37:31
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-03-11 17:46:33
* @LastEditTime: 2025-03-12 15:31:05
* @FilePath: /logic-flow2/src/views/home.vue
* @Description: 文件描述
-->
......@@ -13,6 +13,10 @@
<el-button type="primary" @click="goTo('node-model')">node-model</el-button>
<el-button type="primary" @click="goTo('node-view')">node-view</el-button>
<el-button type="primary" @click="goTo('node-vue')">node-vue</el-button>
<el-button type="primary" @click="goTo('edge')">edge</el-button>
<el-button type="primary" @click="goTo('edge-custom')">edge-custom</el-button>
<el-button type="primary" @click="goTo('edge-text')">edge-text</el-button>
<el-button type="primary" @click="goTo('edge-arrow')">edge-arrow</el-button>
</template>
<script setup>
......