hookehuyr

chore: 更新依赖版本并添加uuid库

更新了esbuild、rollup、vite及其相关插件的版本,并在package.json中添加了uuid库。同时,新增了多个API视图文件和相关逻辑处理文件。
......@@ -20,6 +20,7 @@
"lodash-es": "^4.17.21",
"postcss": "^8.5.3",
"tailwindcss": "^4.0.12",
"uuid": "^11.1.0",
"vue": "^3.5.13",
"vue-router": "^4.5.0"
},
......
/*
* @Date: 2025-03-10 13:07:05
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-03-12 23:35:54
* @LastEditTime: 2025-03-17 15:19:41
* @FilePath: /logic-flow2/src/main.js
* @Description: 文件描述
*/
......@@ -16,13 +16,14 @@ import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import LogicFlow from '@logicflow/core';
import { Menu, DndPanel, SelectionSelect, Control, InsertNodeInPolyline, Highlight } from "@logicflow/extension";
import { Menu, DndPanel, SelectionSelect, Control, InsertNodeInPolyline, Highlight, Label } from "@logicflow/extension";
LogicFlow.use(Menu) // 右键菜单
LogicFlow.use(DndPanel) // 拖拽面板
LogicFlow.use(SelectionSelect) // 选中元素
// LogicFlow.use(Control) // 控制面板
LogicFlow.use(InsertNodeInPolyline) // 边上插入节点
// LogicFlow.use(Highlight) // 高亮
// LogicFlow.use(Label) // Label标签
const app = createApp(App)
app.use(ElementPlus)
......
/*
* @Date: 2025-03-13 18:34:16
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-03-16 00:21:53
* @LastEditTime: 2025-03-19 00:34:12
* @FilePath: /logic-flow2/src/router/index.js
* @Description: 文件描述
*/
......@@ -125,6 +125,26 @@ const router = createRouter({
name: 'api-graphModel',
component: () => import('../views/api/graphModel.vue')
},
{
path: '/api-nodeModel',
name: 'api-nodeModel',
component: () => import('../views/api/nodeModel.vue')
},
{
path: '/api-scalable-node',
name: 'api-scalable-node',
component: () => import('../views/api/scalable-node.vue')
},
{
path: '/api-edge-model',
name: 'api-edge-model',
component: () => import('../views/api/edgeModel.vue')
},
{
path: '/api-transform-model',
name: 'api-transform-model',
component: () => import('../views/api/transformModel.vue')
},
]
})
......
/*
* @Date: 2025-03-17 15:40:00
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-03-18 17:17:47
* @FilePath: /logic-flow2/src/views/api/draggable-text-node.js
* @Description: 可拖动文本的矩形节点
*/
import { RectNode, RectNodeModel } from "@logicflow/core";
import { v4 as uuidv4 } from "uuid";
const useNodeBehavior = () => {
const isTextDraggable = ref(true);
const isTextEditable = ref(false);
const behavior = {
nodeStyle: ref({
stroke: '#1E90FF',
fill: '#F0F8FF',
strokeWidth: 1
}),
selectedStyle: ref({
stroke: '#ff7f0e',
strokeWidth: 2
}),
textStyle: ref({
cursor: 'move'
})
};
return {
isTextDraggable,
isTextEditable,
behavior
};
};
class DraggableTextNodeModel extends RectNodeModel {
initNodeData(data) {
// 确保 data.x 和 data.y 有默认值
const nodeX = data.x || 0;
const nodeY = data.y || 0;
// 处理文本数据
if (!data.text || typeof data.text === "string") {
data.text = {
value: data.text || "",
x: nodeX,
y: nodeY + 80,
};
}
super.initNodeData(data);
const { behavior } = useNodeBehavior();
this.behavior = behavior;
this.text.draggable = true;
this.text.editable = false;
}
getNodeStyle() {
/**
* - 合并基础样式
* - 根据选中状态添加额外样式
*/
return {
...this.behavior.nodeStyle.value,
...(this.isSelected ? this.behavior.selectedStyle.value : {})
};
}
getTextStyle() {
/**
* - 扩展原有文本样式
* - 添加自定义样式(如移动光标)
*/
const style = super.getTextStyle();
return {
...style,
...this.behavior.textStyle.value
};
}
getAnchorStyle() {
const style = super.getAnchorStyle();
style.stroke = "rgb(24, 125, 255)";
style.r = 3;
style.hover.r = 8;
style.hover.fill = "rgb(24, 125, 255)";
style.hover.stroke = "rgb(24, 125, 255)";
return style;
}
getAnchorLineStyle() {
const style = super.getAnchorLineStyle();
style.stroke = "rgb(24, 125, 255)";
return style;
}
getOutlineStyle() {
const style = super.getOutlineStyle();
style.stroke = "red";
style.hover.stroke = "yellow";
return style;
}
createId() {
return `custom-rect-${uuidv4()}`;
}
// 定义节点只有左右两个锚点. 锚点位置通过中心点和宽度算出来。
getDefaultAnchor() {
const { width, height, x, y, id } = this;
return [
{
x: x - width / 2,
y,
name: 'left',
id: `${id}_0`
},
{
x: x + width / 2,
y,
name: 'right',
id: `${id}_1`,
// edgeAddable: false
},
]
}
// getConnectedSourceRules(){
// const rules = super.getConnectedSourceRules();
// const getWayOnlyAsTarget = {
// message: "结束节点只能连入,不能连出!",
// validate: ( source, target, sourceAnchor, targetAnchor ) => {
// let isValid = true;
// if (source) {
// isValid = false;
// }
// return isValid;
// },
// };
// rules.push(getWayOnlyAsTarget);
// return rules;
// }
// getConnectedTargetRules() {
// const rules = super.getConnectedTargetRules();
// const notAsTarget = {
// message: "起始节点不能作为边的终点",
// validate: () => false,
// };
// rules.push(notAsTarget);
// return rules;
// }
isAllowMoveNode(deltaX, deltaY) {
let newX = this.x + deltaX
let newY = this.y + deltaY
let isAllowMoveX = true
let isAllowMoveY = true
// 处理
return {
x: isAllowMoveX,
y: isAllowMoveY,
}
}
// isAllowConnectedAsSource(target, sourceAnchor, targetAnchor) {
// // 根据节点类型判断是否允许连线
// if (this.properties.nodeType === 'source') {
// if (target.properties.nodeType === 'target') {
// return false
// }
// return true
// }
// }
// isAllowConnectedAsTarget(source, sourceAnchor, targetAnchor) {
// // 根据节点类型判断是否允许连线
// if (this.properties.nodeType ==='target') {
// if (source.properties.nodeType === 'source') {
// return false
// }
// return true
// }
// }
}
class DraggableTextNode extends RectNode {}
export default {
type: "custom-rect",
view: DraggableTextNode,
model: DraggableTextNodeModel,
};
<!--
* @Date: 2025-03-10 16:52:35
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-03-18 23:51:14
* @FilePath: /logic-flow2/src/views/api/edgeModel.vue
* @Description: 拖拽面板
-->
<template>
<div class="container">
<div ref="container" class="flow-container"></div>
</div>
</template>
<script setup>
import LogicFlow from '@logicflow/core';
import { PolylineEdge, PolylineEdgeModel } from '@logicflow/core';
class CustomEdgeModel extends PolylineEdgeModel {
getEdgeStyle() {
const style = super.getEdgeStyle();
style.stroke = "blue";
style.strokeDasharray = "3 3";
return style;
}
}
const container = ref(null);
let lf = null;
onMounted(() => {
lf = new LogicFlow({
container: container.value,
grid: true,
});
// 注册自定义边
lf.register({
type: 'custom-edge',
view: PolylineEdge,
model: CustomEdgeModel
});
lf.setDefaultEdgeType('custom-edge');
lf.render({
nodes: [
{ id: 'node1', type: 'rect', x: 200, y: 100 },
{ id: 'node2', type: 'circle', x: 400, y: 100 },
],
edges: [{
id: 'edge1',
type: 'custom-edge',
sourceNodeId: 'node1',
targetNodeId: 'node2'
}],
});
});
</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-18 17:18:57
* @FilePath: /logic-flow2/src/views/api/nodeModel.vue
* @Description: 拖拽面板
-->
<template>
<div class="container">
<div ref="container" class="flow-container"></div>
</div>
</template>
<script setup>
import LogicFlow from "@logicflow/core";
import "@logicflow/extension/lib/style/index.css";
import CustomNode from "./draggable-text-node";
const container = ref(null);
let lf = null;
/**
* 先必须初始化的时把配置项打开,之后再进行单独的数据设置
* 比如你如果要文本移动,那么你必须先打开文本移动的配置项,然后再进行单独的数据设置
*/
onMounted(() => {
lf = new LogicFlow({
container: container.value,
grid: true,
// plugins: [Label], // 引入 Label 插件
nodeTextDraggable: true, // 开启节点文本拖拽
});
// 注册自定义节点
lf.register(CustomNode);
// 设置拖拽面板配置
lf.setPatternItems([
{
type: "custom-rect",
text: "自定义节点",
label: "拖拽生成节点",
className: "custom-node",
},
]);
// 监听节点创建事件,自定义ID规则
lf.on("node:dnd-add", ({ data }) => {
const prefix = "custom_node_";
const timestamp = Date.now();
data.id = `${prefix}${timestamp}`;
console.log("新创建的节点ID:", data.id);
});
// 添加连线事件监听
lf.on('edge:connect', ({ data }) => {
console.log('连线成功', data);
});
lf.on('connection:not-allowed', (data) => {
console.log('连线被阻止', data);
});
lf.render({
nodes: [
{
id: "node1",
type: "custom-rect",
x: 200,
y: 100,
// text: {
// x: 250,
// y: 150,
// value: 'Node 1',
// },
text: "Node 1",
},
{
id: "node2",
type: "circle",
x: 400,
y: 100,
text: {
x: 450,
y: 150,
value: "Node 2",
draggable: false,
},
},
],
edges: [{ id: "edge1", sourceNodeId: "node1", targetNodeId: "node2" }],
});
lf.translateCenter();
lf.on("node:click", ({data}) => {
lf.getNodeModelById(data.id).setProperties({
disabled: !data.properties.disabled,
scale: 2,
});
console.warn(lf.getGraphData());
});
const nodeModel = lf.getNodeModelById("node1");
// const { anchors } = nodeModel;
// nodeModel.setIsShowAnchor(true)
console.warn("nodeModel", nodeModel.getConnectedTargetRules());
});
</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-17 23:33:06
* @FilePath: /logic-flow2/src/views/api/scalable-node.vue
* @Description: 可缩放节点示例
-->
<template>
<div class="container">
<div ref="container" class="flow-container"></div>
</div>
</template>
<script setup>
import LogicFlow from '@logicflow/core';
import ScalableRectNode from './scalable-rect-node';
const container = ref(null);
let lf = null;
onMounted(() => {
lf = new LogicFlow({
container: container.value,
grid: true,
});
// 注册自定义节点
lf.register(ScalableRectNode);
lf.render({
nodes: [
{
id: 'node1',
type: 'scalable-rect',
x: 300,
y: 200,
text: '可缩放节点',
properties: {
scale: 1, // 初始缩放比例
},
},
],
});
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-17 23:23:33
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-03-17 23:35:02
* @FilePath: /logic-flow2/src/views/api/scalable-rect-node.js
* @Description: 文件描述
*/
import { RectResize } from "@logicflow/extension";
class CustomNode extends RectResize.view {
}
class ScalableRectNode extends RectResize.model {
initNodeData(data) {
super.initNodeData(data);
this.width = 80;
this.height = 40;
this.maxWidth = 300;
this.maxHeight = 300;
this.text.draggable = true;
}
// setAttributes() {
// const size = this.properties.scale || 1;
// this.width = 100 * size;
// this.height = 80 * size;
// }
getDefaultAnchor() {
const { width, height, x, y, id } = this;
return [
{ x: x - width / 2, y, id: `${id}_0` },
{ x: x + width / 2, y, id: `${id}_1` },
{ x, y: y - height / 2, id: `${id}_2` },
{ x, y: y + height / 2, id: `${id}_3` },
];
}
}
export default {
type: 'scalable-rect',
view: CustomNode,
model: ScalableRectNode,
};
<!--
* @Date: 2025-03-10 16:52:35
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-03-19 00:57:55
* @FilePath: /logic-flow2/src/views/api/transformModel.vue
* @Description: 拖拽面板
-->
<template>
<div class="container">
<div ref="container" class="flow-container"></div>
<div class="control-panel">
<button @click="zoomIn">放大</button>
<button @click="zoomOut">缩小</button>
<button @click="moveLeft">左移</button>
<button @click="moveRight">右移</button>
<button @click="centerView">居中</button>
</div>
</div>
</template>
<script setup>
import LogicFlow from "@logicflow/core";
const container = ref(null);
let lf = null;
// 缩放画布
const zoomIn = () => {
const { transformModel } = lf.graphModel;
const currentZoom = transformModel.ZOOM;
transformModel.zoom(currentZoom + 0.1);
};
const zoomOut = () => {
const { transformModel } = lf.graphModel;
const currentZoom = transformModel.ZOOM;
transformModel.zoom(currentZoom - 0.1);
};
// 平移画布
const moveLeft = () => {
const { transformModel } = lf.graphModel;
const [x, y] = transformModel.getTranslate();
transformModel.translate(x - 50, y);
};
const moveRight = () => {
const { transformModel } = lf.graphModel;
const [x, y] = transformModel.getTranslate();
transformModel.translate(x + 50, y);
};
// 居中显示
const centerView = () => {
const { transformModel } = lf;
// 直接从 lf 实例获取节点
const nodes = lf.graphModel.nodes;
if (nodes.length === 0) return;
// 计算所有节点的边界框
const bounds = nodes.reduce((acc, node) => {
const { x, y } = node;
acc.minX = Math.min(acc.minX, x);
acc.maxX = Math.max(acc.maxX, x);
acc.minY = Math.min(acc.minY, y);
acc.maxY = Math.max(acc.maxY, y);
return acc;
}, { minX: Infinity, maxX: -Infinity, minY: Infinity, maxY: -Infinity });
// 计算中心点和范围
const centerX = (bounds.minX + bounds.maxX) / 2;
const centerY = (bounds.minY + bounds.maxY) / 2;
const width = bounds.maxX - bounds.minX + 200; // 添加边距
const height = bounds.maxY - bounds.minY + 100;
// 居中显示
transformModel.focusOn(centerX, centerY, width, height);
};
onMounted(() => {
lf = new LogicFlow({
container: container.value,
grid: true,
});
// 监听点击画布功能
lf.on("canvas:click", (e) => {
console.log("Canvas clicked at:", e.x, e.y);
});
lf.on('node:click', (e) => {
console.log(e);
// 示例:HTML坐标转换为画布坐标
console.warn(e);
const htmlPoint = { x: 100, y: 100 };
const { transformModel } = lf.graphModel;
const canvasPoint = transformModel.HtmlPointToCanvasPoint(htmlPoint);
console.log("Canvas coordinates:", canvasPoint);
});
lf.render({
nodes: [
{ id: "node1", type: "rect", x: 200, y: 100 },
{ id: "node2", type: "circle", x: 400, y: 100 },
],
edges: [{ id: "edge1", sourceNodeId: "node1", targetNodeId: "node2" }],
});
// 初始化时居中显示
setTimeout(() => {
const { transformModel } = lf;
// 这里也需要修改
const nodes = lf.graphModel.nodes;
if (nodes.length === 0) return;
const bounds = nodes.reduce((acc, node) => {
const { x, y } = node;
acc.minX = Math.min(acc.minX, x);
acc.maxX = Math.max(acc.maxX, x);
acc.minY = Math.min(acc.minY, y);
acc.maxY = Math.max(acc.maxY, y);
return acc;
}, { minX: Infinity, maxX: -Infinity, minY: Infinity, maxY: -Infinity });
const centerX = (bounds.minX + bounds.maxX) / 2;
const centerY = (bounds.minY + bounds.maxY) / 2;
const width = bounds.maxX - bounds.minX + 200;
const height = bounds.maxY - bounds.minY + 100;
// transformModel.focusOn(centerX, centerY, width, height);
}, 0);
});
</script>
<style scoped>
.container {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
}
.flow-container {
flex: 1;
width: 100%;
height: 100%;
}
.control-panel {
position: fixed;
top: 20px;
left: 20px;
display: flex;
gap: 10px;
}
button {
padding: 8px 16px;
border: none;
border-radius: 4px;
background-color: #4a90e2;
color: white;
cursor: pointer;
}
button:hover {
background-color: #357abd;
}
</style>
This diff is collapsed. Click to expand it.