mirror of
https://github.com/Powerful-517/yys-editor.git
synced 2025-08-23 16:14:51 +00:00
支持图层处理
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, shallowRef, markRaw } from 'vue';
|
||||
import { ref, onMounted, shallowRef, markRaw, onUnmounted } from 'vue';
|
||||
import { VueFlow, useVueFlow, Panel, NodeTypes } from '@vue-flow/core';
|
||||
import { Background } from '@vue-flow/background';
|
||||
import { Controls } from '@vue-flow/controls';
|
||||
@@ -47,34 +47,42 @@ const { nodes, edges, onNodesChange, onEdgesChange, onConnect, addNodes, setTran
|
||||
nodeTypes
|
||||
});
|
||||
|
||||
// 右键菜单相关
|
||||
const contextMenu = ref({
|
||||
show: false,
|
||||
x: 0,
|
||||
y: 0,
|
||||
nodeId: null
|
||||
});
|
||||
|
||||
// 处理拖拽放置
|
||||
const handleDrop = (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
|
||||
try {
|
||||
// 获取拖拽数据
|
||||
const nodeData = JSON.parse(event.dataTransfer.getData('application/json'));
|
||||
|
||||
|
||||
// 获取画布元素
|
||||
const flowContainer = event.currentTarget;
|
||||
const bounds = flowContainer.getBoundingClientRect();
|
||||
|
||||
|
||||
// 获取画布的缩放和偏移信息
|
||||
const { x: viewportX, y: viewportY, zoom } = getViewport();
|
||||
|
||||
|
||||
// 计算相对于画布的位置,并考虑缩放和偏移
|
||||
const position = {
|
||||
x: (event.clientX - bounds.left - viewportX) / zoom,
|
||||
y: (event.clientY - bounds.top - viewportY) / zoom
|
||||
};
|
||||
|
||||
|
||||
// 创建新节点
|
||||
const newNode = {
|
||||
...nodeData,
|
||||
position,
|
||||
selected: true // 设置节点为选中状态
|
||||
};
|
||||
|
||||
|
||||
// 添加节点
|
||||
handleAddNode(newNode);
|
||||
} catch (error) {
|
||||
@@ -96,7 +104,7 @@ const handleAddNode = (newNode) => {
|
||||
updateNode(node.id, { selected: false });
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// 根据节点类型设置初始数据
|
||||
let initialData = {};
|
||||
switch (newNode.type) {
|
||||
@@ -149,7 +157,7 @@ const handleAddNode = (newNode) => {
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// 添加新节点,并设置为选中状态
|
||||
addNodes([{
|
||||
...newNode,
|
||||
@@ -159,7 +167,7 @@ const handleAddNode = (newNode) => {
|
||||
...initialData
|
||||
}
|
||||
}]);
|
||||
|
||||
|
||||
// 重新设置视图,使新节点可见
|
||||
setTransform({ x: 0, y: 0, zoom: 1 });
|
||||
};
|
||||
@@ -179,8 +187,84 @@ const handleOpenPropertySelect = (node) => {
|
||||
emit('open-property-select', node);
|
||||
};
|
||||
|
||||
// 处理节点右键点击
|
||||
const handleNodeContextMenu = (event) => {
|
||||
const { event: mouseEvent, node } = event;
|
||||
mouseEvent.preventDefault();
|
||||
mouseEvent.stopPropagation();
|
||||
|
||||
contextMenu.value = {
|
||||
show: true,
|
||||
x: mouseEvent.clientX,
|
||||
y: mouseEvent.clientY,
|
||||
nodeId: node.id
|
||||
};
|
||||
};
|
||||
|
||||
// 处理画布右键点击
|
||||
const handlePaneContextMenu = (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
contextMenu.value.show = false;
|
||||
};
|
||||
|
||||
// 点击其他地方时关闭菜单
|
||||
const handleClickOutside = (event) => {
|
||||
if (!event.target.closest('.context-menu')) {
|
||||
contextMenu.value.show = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 处理图层顺序调整
|
||||
const handleLayerOrder = (action) => {
|
||||
if (!contextMenu.value.nodeId) return;
|
||||
|
||||
const nodeId = contextMenu.value.nodeId;
|
||||
const nodeIndex = nodes.value.findIndex(n => n.id === nodeId);
|
||||
if (nodeIndex === -1) return;
|
||||
|
||||
const node = nodes.value[nodeIndex];
|
||||
const newNodes = [...nodes.value];
|
||||
|
||||
switch (action) {
|
||||
case 'bringToFront':
|
||||
// 移至最前
|
||||
newNodes.splice(nodeIndex, 1);
|
||||
newNodes.push(node);
|
||||
break;
|
||||
case 'sendToBack':
|
||||
// 移至最后
|
||||
newNodes.splice(nodeIndex, 1);
|
||||
newNodes.unshift(node);
|
||||
break;
|
||||
case 'bringForward':
|
||||
// 上移一层
|
||||
if (nodeIndex < newNodes.length - 1) {
|
||||
[newNodes[nodeIndex], newNodes[nodeIndex + 1]] = [newNodes[nodeIndex + 1], newNodes[nodeIndex]];
|
||||
}
|
||||
break;
|
||||
case 'sendBackward':
|
||||
// 下移一层
|
||||
if (nodeIndex > 0) {
|
||||
[newNodes[nodeIndex], newNodes[nodeIndex - 1]] = [newNodes[nodeIndex - 1], newNodes[nodeIndex]];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// 更新节点顺序
|
||||
nodes.value = newNodes;
|
||||
contextMenu.value.show = false;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
console.log('FlowEditor 组件已挂载');
|
||||
// 添加全局点击事件监听
|
||||
document.addEventListener('click', handleClickOutside);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
// 移除事件监听
|
||||
document.removeEventListener('click', handleClickOutside);
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -191,18 +275,20 @@ onMounted(() => {
|
||||
<div class="components-sidebar">
|
||||
<ComponentsPanel @add-node="handleAddNode" />
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 中间流程图区域 -->
|
||||
<div class="flow-container">
|
||||
<VueFlow
|
||||
:nodes="nodes"
|
||||
:edges="edges"
|
||||
@nodes-change="onNodesChange"
|
||||
@edges-change="onEdgesChange"
|
||||
@connect="onConnect"
|
||||
fit-view-on-init
|
||||
@drop="handleDrop"
|
||||
@dragover="handleDragOver"
|
||||
<VueFlow
|
||||
:nodes="nodes"
|
||||
:edges="edges"
|
||||
@nodes-change="onNodesChange"
|
||||
@edges-change="onEdgesChange"
|
||||
@connect="onConnect"
|
||||
fit-view-on-init
|
||||
@drop="handleDrop"
|
||||
@dragover="handleDragOver"
|
||||
@node-context-menu="handleNodeContextMenu"
|
||||
@pane-context-menu="handlePaneContextMenu"
|
||||
>
|
||||
<Background pattern-color="#aaa" gap="8" />
|
||||
<Controls />
|
||||
@@ -210,14 +296,27 @@ onMounted(() => {
|
||||
<div>流程图编辑器 (模仿 draw.io)</div>
|
||||
</Panel>
|
||||
</VueFlow>
|
||||
|
||||
<!-- 右键菜单 -->
|
||||
<Teleport to="body">
|
||||
<div v-if="contextMenu.show"
|
||||
class="context-menu"
|
||||
:style="{ left: contextMenu.x + 'px', top: contextMenu.y + 'px' }"
|
||||
@click.stop>
|
||||
<div class="menu-item" @click="handleLayerOrder('bringToFront')">移至最前</div>
|
||||
<div class="menu-item" @click="handleLayerOrder('sendToBack')">移至最后</div>
|
||||
<div class="menu-item" @click="handleLayerOrder('bringForward')">上移一层</div>
|
||||
<div class="menu-item" @click="handleLayerOrder('sendBackward')">下移一层</div>
|
||||
</div>
|
||||
</Teleport>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 右侧属性面板 -->
|
||||
<PropertyPanel
|
||||
:height="height"
|
||||
@open-shikigami-select="handleOpenShikigamiSelect"
|
||||
@open-yuhun-select="handleOpenYuhunSelect"
|
||||
@open-property-select="handleOpenPropertySelect"
|
||||
<PropertyPanel
|
||||
:height="height"
|
||||
@open-shikigami-select="handleOpenShikigamiSelect"
|
||||
@open-yuhun-select="handleOpenYuhunSelect"
|
||||
@open-property-select="handleOpenPropertySelect"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -268,4 +367,29 @@ onMounted(() => {
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.context-menu {
|
||||
position: fixed;
|
||||
background: white;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
||||
padding: 5px 0;
|
||||
z-index: 9999;
|
||||
min-width: 120px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
padding: 8px 16px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.menu-item:hover {
|
||||
background-color: #f5f7fa;
|
||||
color: #409eff;
|
||||
}
|
||||
</style>
|
Reference in New Issue
Block a user