tangjunhao vor 3 Monaten
Ursprung
Commit
f995bb6a1e

+ 6 - 1
src/views/model/vueflow/defaultnode.vue

@@ -33,7 +33,7 @@ onMounted(() => {
 <style scoped> 
 
 .icons img{
-    width: 26px;
+    width: 20px;
 
 }
 .icons span{
@@ -65,6 +65,11 @@ onMounted(() => {
 .vue-flow__node {
           stroke: none; /* 移除节点边框 */
         }
+
+.vue-flow__node:hover{
+  /* 移除 hover 时的阴影 */
+  box-shadow: none !important;
+}
         
  
 .vue-flow__edge-text {

+ 258 - 18
src/views/model/vueflow/index.vue

@@ -66,9 +66,11 @@ import "./main.css";//重置样式
 import defaultnode from './defaultnode.vue'
 import PointOnlyNode from './pointonlynode.vue'
 import useDragAndDrop from './useDnD';
+import {getNodeCount, setNodeCount} from './useDnD';
 import changebak from './changebak.vue'
 import { useProjectStore } from '@/store/project'
 import emitter from "@/utils/emitter";
+import { onMounted } from 'vue';
 
 const { onInit, onNodeDragStop, onConnect, addEdges, setViewport, toObject,addNodes,updateEdgeData,onConnectStart} = useVueFlow()
 
@@ -84,18 +86,83 @@ let mergedObj = ref('');
 
 // 选中节点
 let noid = ref([]);
-let Edgeid = ref();
+let Edgeid = ref();//选中线段id
 let seledge=ref(null);
+let previousEdge = null;  // 用于保存上一个选中的边缘
 // 连线颜色
 let linecolor=ref('#2267B1')
 // 线宽
 let linewidth=ref(1);
 
+let midNodeCounter = 0;//储存中间节点的序号
+
 
 const projectStore = useProjectStore()
 let pid = computed(() => projectStore.pid || '')
 
 
+// 旧数据存储
+let prevNodes = [...nodes.value];
+let prevEdges = [...edges.value];
+
+// 监听节点变化
+watch(nodes, (newNodes) => {
+  const deletedNodes = prevNodes.filter((node) => !newNodes.some((n) => n.id === node.id));
+  const addedNodes = newNodes.filter((node) => !prevNodes.some((n) => n.id === node.id));
+
+  if (deletedNodes.length > 0) {
+    console.log('Deleted Nodes:', deletedNodes);
+    deletedNodes.forEach((node) => {
+      if (node.data?.pcId) {
+        comdelete(node.data.pcId);
+      }
+    });
+  }
+  if (addedNodes.length > 0) {
+    // console.log('Added Nodes:', addedNodes);
+  }
+
+  // 更新快照
+  prevNodes = [...newNodes];
+}, { deep: true });
+
+// 监听连线变化
+watch(edges, (newEdges) => {
+  const deletedEdges = prevEdges.filter((edge) => !newEdges.some((e) => e.id === edge.id));
+  const addedEdges = newEdges.filter((edge) => !prevEdges.some((e) => e.id === edge.id));
+
+  if (deletedEdges.length > 0) {
+    console.log('Deleted Edges:', deletedEdges);
+    deletedEdges.forEach((edge) => {
+      const sourceNode = prevNodes.find(n => n.id === edge.source);
+      const targetNode = prevNodes.find(n => n.id === edge.target);
+
+      if (!sourceNode || !targetNode) return;
+
+      let npcId = '';
+      let pcId = '';
+
+      // 判断 source 或 target 是否为中间点 comId === '3'
+      if (sourceNode.data?.comId === '3') {
+        npcId = sourceNode.data.npcId;
+        pcId = targetNode.data?.pcId;
+      } else if (targetNode.data?.comId === '3') {
+        npcId = targetNode.data.npcId;
+        pcId = sourceNode.data?.pcId;
+      }
+
+      if (npcId && pcId) {
+        comlinedelete(npcId, pcId);
+      }
+    });
+  }
+  if (addedEdges.length > 0) {
+    console.log('Added Edges:', addedEdges);
+  }
+
+  // 更新快照
+  prevEdges = [...newEdges];
+}, { deep: true });
 
 
 const resetTransform = () => {
@@ -112,7 +179,12 @@ const toggleDarkMode = () => {
 }
 
 const saveproject = () => {
-  let obj = { nodes: toObject().nodes,edges:toObject().edges };
+  let obj = { 
+    nodes: toObject().nodes,
+    edges: toObject().edges,
+    midNodeCounter: midNodeCounter, // 添加中间节点计数器
+    nodeCount: getNodeCount(), // 获取当前节点计数
+  };
   mergedObj.value=JSON.stringify(obj);
 
   const params = {
@@ -125,6 +197,10 @@ const saveproject = () => {
   };
   request(params)
     .then((res) => {
+      projectStore.setProjectInfo({
+        ...projectStore.projectInfo,
+        flow: mergedObj.value || '',
+      })
       
     })
     .catch((error) => {
@@ -133,15 +209,84 @@ const saveproject = () => {
 }
 
 const removeNode = () => {
+  if (!noid.value) {
+    console.warn('未选中任何节点');
+    return;
+  }
+
+  const nodeIdToRemove = noid.value.id;
+
+  vueFlowRef.value.removeNodes(noid.value.id);
 
+  // 清空当前选中
+  noid.value = null;
+
+  saveproject();
 }
 
 const removeEdge = () => {
+  if (!seledge.value) {
+    ElMessage.warning('请先选择要删除的连线');
+    return;
+  }
+
+  vueFlowRef.value.removeEdges(seledge.value); // 移除选中的边
+  seledge.value = null; // 清空选中状态
 
+  saveproject();
 }
 
 const confirmDelete = () => {
+  ElMessageBox.confirm(
+    '确定要删除所有节点和连线吗?此操作不可恢复!',
+    '警告',
+    {
+      confirmButtonText: '确认',
+      cancelButtonText: '取消',
+      type: 'warning',
+    }
+  )
+    .then(() => {
+
+      vueFlowRef.value.removeNodes(nodes.value)
+      vueFlowRef.value.removeEdges(edges.value)
 
+      ElMessage.success('所有节点和连线已删除')
+      saveproject();
+    })
+    .catch(() => {
+      ElMessage.info('已取消删除')
+    })
+}
+
+const comdelete = (pcId) => {
+  const params = {
+    transCode:'ES0005',
+    pcId: pcId
+  }
+  request(params)
+    .then((res) => {
+      // console.log('组件删除成功')
+    })
+    .catch((err) => {
+      ElMessage.error(err.returnMsg);
+    });
+}
+
+const comlinedelete = (npcId,pcId) => {
+  const params = {
+    transCode:'ES0005',
+    npcId: npcId,
+    pcId: pcId,
+    type: 0
+  }
+  request(params)
+    .then((res) => {
+      // console.log('组件连线删除成功')
+    })
+    .catch((err) => {
+      ElMessage.error(err.returnMsg);
+    });
 }
 
 
@@ -154,12 +299,42 @@ const customOnDrop = (event) => {
 
 
 
-const onNodeContextMenu = ( event) => {
-  console.log('右键点击', event);
+const onNodeContextMenu = ( e) => {
+  console.log('右键点击', e);
 }
 
-const onEdgeClick = (event) => {
-  console.log('边数据:', event);
+const onEdgeClick = (e) => {
+  console.log('Edge Click', e.edge);
+  // console.log('所有线段:', edges.value);
+  // 如果已经有选中的边缘
+  if (seledge.value) {
+    // 恢复上一个选中边缘的样式
+    if (previousEdge) {
+      previousEdge.style = {
+        ...previousEdge.style,
+        stroke: previousEdge.originalColor,  // 恢复原始颜色
+        strokeWidth: previousEdge.originalWidth,// 恢复原始宽度
+      };
+    }
+  }
+
+  // 保存当前点击的边缘为选中边缘
+  Edgeid.value = e.edge.id;
+  seledge.value = e.edge;
+
+  // 暂时更改当前选中边缘的样式
+  seledge.value.originalColor = seledge.value.style.stroke;  // 保存当前边缘的原始颜色
+  seledge.value.originalWidth = seledge.value.style.strokeWidth; // 保存当前边缘的原始宽度
+
+  const isdefault = e.edge.data.type === 'default'; // 判断是否为默认线
+  seledge.value.style = {
+    ...seledge.value.style,
+    stroke: isdefault ? '#2267B1' : 'rgba(255, 255, 0, 0.3)',// 设置选中边缘的颜色
+    strokeWidth: isdefault ? 2 : 6,// 设置选中边缘的宽度
+  };
+
+  // 保存当前选中的边缘作为上一个选中边缘
+  previousEdge = seledge.value;
 }
 
 const onNodeClick = (event) => {
@@ -176,6 +351,11 @@ const onEdgeDoubleClick = (event) => {
   noid.value = event.node;
 }
 
+// 监听连接开始,提前取消选中的线段
+onConnectStart(() => {
+  cleanEdgeselect();
+});
+
 // 线的类型 default
 let lineType = ref('default');
 
@@ -193,6 +373,11 @@ onConnect(async (connection) => {
   const sourceNode = vueFlowRef.value.getNode(connection.source);
   const targetNode = vueFlowRef.value.getNode(connection.target);
 
+  if(sourceNode.data.comId === '3' && targetNode.data.comId === '3'){
+    console.warn('禁止连接两个中间节点');
+    return;
+  }
+
   // 是否需要中间节点
   const isCom3 = sourceNode.data.comId === '3' || targetNode.data.comId === '3';
 
@@ -243,13 +428,36 @@ onConnect(async (connection) => {
     const midX = (sourceCenterX + targetCenterX) / 2;
     const midY = (sourceCenterY + targetCenterY) / 2;
 
+    // 计算 sourceNode 和 targetNode 相对于 pointNode 的位置
+    const dxSource = sourceNode.position.x - midX;
+    const dySource = sourceNode.position.y - midY;
+    const dxTarget = targetNode.position.x - midX;
+    const dyTarget = targetNode.position.y - midY;
+
+    // 判断 sourceNode 的主要方向(水平 or 垂直)
+    const isSourceHorizontal = Math.abs(dxSource) > Math.abs(dySource);
+    const sourceDirection = isSourceHorizontal 
+    ? (dxSource < 0 ? 'left' : 'right') 
+    : (dySource < 0 ? 'top' : 'bottom');
+
+    // 判断 targetNode 的主要方向
+    const isTargetHorizontal = Math.abs(dxTarget) > Math.abs(dyTarget);
+    const targetDirection = isTargetHorizontal 
+    ? (dxTarget < 0 ? 'left' : 'right') 
+    : (dyTarget < 0 ? 'top' : 'bottom');
+
+
+    midNodeCounter++;
+    const uid = `N${midNodeCounter}`;
+
     nodes.value.push({
       id: pointNodeId,
       type: 'point-only',
       position: { x: midX, y: midY },
       data: {
         comId:'3',
-        label: '节点'
+        label: '节点',
+        uid: uid,
        },
     });
 
@@ -269,7 +477,7 @@ onConnect(async (connection) => {
         source: connection.source,
         sourceHandle: connection.sourceHandle,
         target: pointNodeId,
-        targetHandle: 'source-left',
+        targetHandle: `source-${sourceDirection}`,
         type: 'smoothstep',
         color: linecolor.value,
         style: { strokeWidth: linewidth.value, stroke: linecolor.value }
@@ -280,7 +488,7 @@ onConnect(async (connection) => {
       const edge2 = {
         id: edgeId2,
         source: pointNodeId,
-        sourceHandle: 'source-right', // 从中间点的右边出发
+        sourceHandle: `source-${targetDirection}`, // 使用计算的方向
         target: connection.target,
         targetHandle: connection.targetHandle,
         type: 'smoothstep',
@@ -315,6 +523,8 @@ onConnect(async (connection) => {
       console.error('中间点处理失败:', err);
     }
   }
+
+  saveproject();
 });
 
 const createEdge = async (pid, type, npcId, pcId1, pcId2) => {
@@ -357,18 +567,48 @@ const createCom3 = async (pid) => {
 }
 
 
-const flowInit=()=>{
-  let nodesflow=JSON.parse(projectStore.projectInfo.flow)
-  nodes.value=nodesflow.nodes;
-  edges.value=nodesflow.edges;
+const flowInit = () => {
+  let nodesflow = JSON.parse(projectStore.projectInfo.flow || '{"nodes":[],"edges":[]}');
+
+  console.log('初始化流程数据:', nodesflow);
+  nodes.value = nodesflow.nodes;
+  edges.value = nodesflow.edges;
+  // 提取中间节点计数器
+  midNodeCounter = nodesflow.midNodeCounter || 0;
+  // 提取节点计数
+  setNodeCount(nodesflow.nodeCount || 0);
 }
 
-const onMounted = () => {
-  setTimeout(function() {
-    flowInit();
-  }, 1500);
+const cleanEdgeselect = () => {
+  if(seledge.value) {
+    // 恢复选中边缘的原始样式
+    seledge.value.style = {
+          ...seledge.value.style,
+          stroke: seledge.value.originalColor,
+          strokeWidth: previousEdge?.originalWidth || 1, // 恢复原始宽度
+        };
 
-};
+    // 清空选中的边缘
+    seledge.value = null;
+    Edgeid.value = null;
+    previousEdge = null;
+  }
+  
+} 
+
+onMounted(() => {
+  flowInit();
+
+  // 点击其他区域取消线段选中
+  if (vueFlowRef.value) {
+    vueFlowRef.value.$el.addEventListener('click', (event) => {
+      // 确保点击的不是边缘
+      if (seledge.value && !event.target.closest('.vue-flow__edge')) {
+        cleanEdgeselect();
+      }
+    });
+  }
+});
 
 
 </script>

+ 25 - 13
src/views/model/vueflow/pointonlynode.vue

@@ -8,29 +8,41 @@ const props = defineProps({
 
 <template>
   <div class="point-only-node" :id="`node-${props.node.id}`">
+    <div class="custom-node icons  " :id="`node-${node.id}`"  >
+      <img :src="props.node.data.image"/>
+      <span>{{props.node.data.uid }}</span>
+    </div>
     <!-- 连接点 -->
-    <Handle id="source-top" type="source" :position="Position.Top" :style="{ top: '2px' }"/>
-    <Handle id="source-right" type="source" :position="Position.Right" :style="{ right: '2px' }"/>
-    <Handle id="source-bottom" type="source" :position="Position.Bottom" :style="{ bottom: '2px' }"/>
-    <Handle id="source-left" type="source" :position="Position.Left" :style="{ left: '2px' }"/>
+    <Handle id="source-top" type="source" :position="Position.Top" />
+    <Handle id="source-right" type="source" :position="Position.Right" />
+    <Handle id="source-bottom" type="source" :position="Position.Bottom" />
+    <Handle id="source-left" type="source" :position="Position.Left" />
   </div>
 </template>
 
 <style scoped>
+.icons img{
+    width: 26px;
+
+}
+.icons span{
+  display: block;
+  font-size: 8px;
+}
+
 .point-only-node {
-  width: 5px;
-  height: 5px;
-  background-color: #555;
+  width: 20px;
+  height: 20px;
+  /* background-color: #555; */
   border-radius: 50%;
   position: relative;
 }
 
-.vue-flow__handle {
-  width: 2px;
-  height: 2px;
-  background-color: #555;
-  border-radius: 50%;
-  border: none;
+</style>
+
+<style>
+.vue-flow__node-point-only.selected{
+  border:1px solid #1a192b;
 }
 
 </style>

+ 15 - 8
src/views/model/vueflow/useDnD.js

@@ -11,7 +11,7 @@ import tempic from "@/assets/img/temp.png"
 const projectStore = useProjectStore()
 let nid = 0
 // 用于计数
-let id = 0
+let nodecount = 0
 let treeobj = ref([])
 let datas = {}
 
@@ -19,13 +19,21 @@ let datas = {}
 // 用于存储组件的临时数据
 let com = {}
 
+export function getNodeCount() {
+  return nodecount;
+}
+
+export function setNodeCount(val) {
+  nodecount = val;
+  console.log("nodecount:",nodecount)
+}
+
 
 /**
  * @returns {string} - A unique id.
  */
 function getId() {
-  //return `node_${id++}`
-  return `${nid}${id++}`
+  return `${nid}${nodecount++}`
 }
 function imagefun() {
   console.log(nid)
@@ -85,8 +93,8 @@ export default function useDragAndDrop() {
 
   function onDragStart(event, type, data) {
     if (event.dataTransfer) {
-      com = data
-      nid = data.comId
+      com = data//传递组件数据
+      nid = data.comId//传递组件ID
       event.dataTransfer.setData("application/vueflow", type)
       event.dataTransfer.effectAllowed = "move"
     }
@@ -139,7 +147,7 @@ export default function useDragAndDrop() {
     })
 
     const pid = projectStore.pid
-    console.log('Store:', projectStore)
+    // console.log('Store:', projectStore)
     const newpcId = ""
 
     const nodeId = getId()
@@ -152,8 +160,7 @@ export default function useDragAndDrop() {
       nameCount[componentName] = 0
     }
 
-    nameCount[componentName]++ // 增加计数
-    const newName = `${componentName}${nameCount[componentName]}` // 新的名称加上序号
+    const newName = `${componentName}${nodecount}` // 新的名称加上序号
 
     let snodes = ref([])
     let sedges = ref([])

+ 3 - 2
src/views/project/index.vue

@@ -276,12 +276,13 @@ const confirmSelected = () => {
 
   // 获取第一个选中项的pid(或根据需要处理多个选中项)
   const pid = selectedRows.value[0].pid
-  console.log('选中的项目:', selectedRows.value[0])
+  // console.log('选中的项目:', selectedRows.value[0])
   projectStore.setpid(pid)
   projectStore.setProjectInfo({
     name: selectedRows.value[0].name,
     keywords: selectedRows.value[0].keywords,
-    remark: selectedRows.value[0].remark
+    remark: selectedRows.value[0].remark,
+    flow: selectedRows.value[0].flow || '',
   })
 
   router.push({