CzRger vor 3 Monaten
Ursprung
Commit
fca646ef81

Datei-Diff unterdrückt, da er zu groß ist
+ 8497 - 0
package-lock.json


+ 1 - 1
package.json

@@ -30,7 +30,7 @@
     "vite-plugin-svg-icons": "^2.0.1",
     "vite-plugin-top-level-await": "^1.4.4",
     "vue": "^3.5.13",
-    "vue-router": "^4.5.0",
+    "vue-router": "^4.5.0"
   },
   "devDependencies": {
     "@vitejs/plugin-vue": "^5.2.1",

+ 5 - 15
src/assets/svg/czr_add.svg

@@ -1,16 +1,6 @@
-<svg width="17" height="16" viewBox="0 0 17 16" xmlns="http://www.w3.org/2000/svg">
-<g id="icon/&#230;&#150;&#176;&#229;&#162;&#158;&#229;&#136;&#151;&#232;&#161;&#168;" clip-path="url(#clip0_86_14667)">
-<g id="Group 4">
-<path id="Subtract" d="M2.76611 5.5625C2.48997 5.5625 2.26611 5.78636 2.26611 6.0625V14C2.26611 14.5523 2.71383 15 3.26611 15H13.2661C13.8184 15 14.2661 14.5523 14.2661 14V2C14.2661 1.44772 13.8184 1 13.2661 1H6.82959C6.55345 1 6.32959 1.22386 6.32959 1.5V1.5C6.32959 1.77614 6.55345 2 6.82959 2H13.2661V14H3.26611L3.26611 6.0625C3.26611 5.78636 3.04226 5.5625 2.76611 5.5625V5.5625Z"/>
-<path id="Union" fill-rule="evenodd" clip-rule="evenodd" d="M4.76611 1.5C4.76611 1.22386 4.54226 1 4.26611 1C3.98997 1 3.76611 1.22386 3.76611 1.5V2.48438H2.76611C2.48997 2.48438 2.26611 2.70823 2.26611 2.98438C2.26611 3.26052 2.48997 3.48438 2.76611 3.48438H3.76611V4.5C3.76611 4.77614 3.98997 5 4.26611 5C4.54226 5 4.76611 4.77614 4.76611 4.5V3.48438H5.76611C6.04226 3.48438 6.26611 3.26052 6.26611 2.98438C6.26611 2.70823 6.04226 2.48438 5.76611 2.48438H4.76611V1.5Z" />
-<path id="Vector 1 (Stroke)" fill-rule="evenodd" clip-rule="evenodd" d="M4.76611 6.60938C4.76611 6.33323 4.98997 6.10938 5.26611 6.10938H11.2661C11.5423 6.10938 11.7661 6.33323 11.7661 6.60938C11.7661 6.88552 11.5423 7.10938 11.2661 7.10938H5.26611C4.98997 7.10938 4.76611 6.88552 4.76611 6.60938Z"/>
-<path id="Vector 2 (Stroke)" fill-rule="evenodd" clip-rule="evenodd" d="M4.76611 9.23438C4.76611 8.95823 4.98997 8.73438 5.26611 8.73438H11.2661C11.5423 8.73438 11.7661 8.95823 11.7661 9.23438C11.7661 9.51052 11.5423 9.73438 11.2661 9.73438H5.26611C4.98997 9.73438 4.76611 9.51052 4.76611 9.23438Z"/>
-<path id="Vector 3 (Stroke)" fill-rule="evenodd" clip-rule="evenodd" d="M4.76611 11.8594C4.76611 11.5832 4.98997 11.3594 5.26611 11.3594H11.2661C11.5423 11.3594 11.7661 11.5832 11.7661 11.8594C11.7661 12.1355 11.5423 12.3594 11.2661 12.3594H5.26611C4.98997 12.3594 4.76611 12.1355 4.76611 11.8594Z"/>
-</g>
-</g>
-<defs>
-<clipPath id="clip0_86_14667">
-<rect width="16" height="16" transform="translate(0.266113)"/>
-</clipPath>
-</defs>
+<svg t="1745399925765" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3429"
+     width="200" height="200">
+  <path
+    d="M912 592h-320v320a80 80 0 0 1-160 0v-320h-320a80 80 0 0 1 0-160h320v-320a80 80 0 0 1 160 0v320h320a80 80 0 0 1 0 160z"
+    p-id="3430"></path>
 </svg>

+ 16 - 0
src/assets/svg/czr_add2.svg

@@ -0,0 +1,16 @@
+<svg width="17" height="16" viewBox="0 0 17 16" xmlns="http://www.w3.org/2000/svg">
+<g id="icon/&#230;&#150;&#176;&#229;&#162;&#158;&#229;&#136;&#151;&#232;&#161;&#168;" clip-path="url(#clip0_86_14667)">
+<g id="Group 4">
+<path id="Subtract" d="M2.76611 5.5625C2.48997 5.5625 2.26611 5.78636 2.26611 6.0625V14C2.26611 14.5523 2.71383 15 3.26611 15H13.2661C13.8184 15 14.2661 14.5523 14.2661 14V2C14.2661 1.44772 13.8184 1 13.2661 1H6.82959C6.55345 1 6.32959 1.22386 6.32959 1.5V1.5C6.32959 1.77614 6.55345 2 6.82959 2H13.2661V14H3.26611L3.26611 6.0625C3.26611 5.78636 3.04226 5.5625 2.76611 5.5625V5.5625Z"/>
+<path id="Union" fill-rule="evenodd" clip-rule="evenodd" d="M4.76611 1.5C4.76611 1.22386 4.54226 1 4.26611 1C3.98997 1 3.76611 1.22386 3.76611 1.5V2.48438H2.76611C2.48997 2.48438 2.26611 2.70823 2.26611 2.98438C2.26611 3.26052 2.48997 3.48438 2.76611 3.48438H3.76611V4.5C3.76611 4.77614 3.98997 5 4.26611 5C4.54226 5 4.76611 4.77614 4.76611 4.5V3.48438H5.76611C6.04226 3.48438 6.26611 3.26052 6.26611 2.98438C6.26611 2.70823 6.04226 2.48438 5.76611 2.48438H4.76611V1.5Z" />
+<path id="Vector 1 (Stroke)" fill-rule="evenodd" clip-rule="evenodd" d="M4.76611 6.60938C4.76611 6.33323 4.98997 6.10938 5.26611 6.10938H11.2661C11.5423 6.10938 11.7661 6.33323 11.7661 6.60938C11.7661 6.88552 11.5423 7.10938 11.2661 7.10938H5.26611C4.98997 7.10938 4.76611 6.88552 4.76611 6.60938Z"/>
+<path id="Vector 2 (Stroke)" fill-rule="evenodd" clip-rule="evenodd" d="M4.76611 9.23438C4.76611 8.95823 4.98997 8.73438 5.26611 8.73438H11.2661C11.5423 8.73438 11.7661 8.95823 11.7661 9.23438C11.7661 9.51052 11.5423 9.73438 11.2661 9.73438H5.26611C4.98997 9.73438 4.76611 9.51052 4.76611 9.23438Z"/>
+<path id="Vector 3 (Stroke)" fill-rule="evenodd" clip-rule="evenodd" d="M4.76611 11.8594C4.76611 11.5832 4.98997 11.3594 5.26611 11.3594H11.2661C11.5423 11.3594 11.7661 11.5832 11.7661 11.8594C11.7661 12.1355 11.5423 12.3594 11.2661 12.3594H5.26611C4.98997 12.3594 4.76611 12.1355 4.76611 11.8594Z"/>
+</g>
+</g>
+<defs>
+<clipPath id="clip0_86_14667">
+<rect width="16" height="16" transform="translate(0.266113)"/>
+</clipPath>
+</defs>
+</svg>

+ 1 - 1
src/components/czr-ui/CzrButton.vue

@@ -13,7 +13,7 @@
     </template>
     <template v-else-if="type == 'add'">
       <div class="czr-button __hover">
-        <SvgIcon name="czr_add" color="var(--czr-main-color)" size="12"/>{{title || '新增'}}
+        <SvgIcon name="czr_add2" color="var(--czr-main-color)" size="12"/>{{title || '新增'}}
       </div>
     </template>
     <template v-else-if="type == 'del'">

+ 167 - 65
src/views/draw/chart/index.vue

@@ -8,6 +8,7 @@
     <div class="chart-block" ref="ref_chart">
       <template v-for="n in state.nodes">
         <node
+          :item="n"
           :type="n.type"
           v-model:x="n.x"
           v-model:y="n.y"
@@ -47,19 +48,10 @@
 <script setup lang="ts">
 import {computed, getCurrentInstance, onMounted, reactive, ref} from "vue";
 import node from '../node/index.vue'
+import {LineDef, NodeDef} from "@/views/draw/config/types";
+import {nodeStyleConfig} from "@/views/draw/config/config";
+import {IfLevelsCondition} from "@/views/draw/node/component/if/type";
 
-type Node = {
-  id: string
-  type: string
-  x: number
-  y: number
-  w: number
-  h: number
-}
-type Line = {
-  source: string
-  target: string
-}
 
 const {proxy} = getCurrentInstance()
 const markerStyle = {
@@ -71,59 +63,158 @@ const state: any = reactive({
   nodes: [
     {
       id: '8facce0d-c5ff-42ba-b6a0-6a920510c794',
-      type: 'root',
+      type: 'NodeRoot',
       x: 0,
       y: 0,
-      w: 240,
-      h: 50,
     },
     {
       id: 'f001c786-c4a0-402f-a79d-19456f9346a6',
-      type: 'type-1',
+      type: 'NodeIf',
       x: 300,
       y: 0,
-      w: 240,
-      h: 100,
+      data: {
+        title: '条件分支1',
+        levels: [
+          {
+            id: '418af28e-8799-45f3-8c26-29bbbad37cbf',
+            method: 'AND',
+            conditions: [
+              {
+                source: 'asd',
+                to: 'zxc',
+                target: 'qwe',
+                targetType: 'value'
+              },
+              {
+                source: 'asd',
+                to: 'zxc',
+                target: 'qwe',
+                targetType: 'value'
+              },
+              {
+                source: 'asd',
+                to: 'zxc',
+                target: 'qwe',
+                targetType: 'value'
+              },
+            ]
+          },
+          {
+            id: 'b812382b-f1bd-4539-a8a3-0efc875107f9',
+            method: 'OR',
+            conditions: [
+              {
+                source: 'asd',
+                to: 'zxc',
+                target: 'qwe',
+                targetType: 'value'
+              },
+              {
+                source: 'asd',
+                to: 'zxc',
+                target: 'qwe',
+                targetType: 'value'
+              },
+              {
+                source: 'asd',
+                to: 'zxc',
+                target: 'qwe',
+                targetType: 'value'
+              },
+            ]
+          },
+          {
+            id: '883881a6-d993-4f5f-ba4f-f382cda4395c',
+            method: 'AND',
+            conditions: [
+              {
+                source: 'asd',
+                to: 'zxc',
+                target: 'qwe',
+                targetType: 'value'
+              },
+              {
+                source: 'asd',
+                to: 'zxc',
+                target: 'qwe',
+                targetType: 'value'
+              },
+              {
+                source: 'asd',
+                to: 'zxc',
+                target: 'qwe',
+                targetType: 'value'
+              },
+            ]
+          }
+        ]
+      }
     },
     {
       id: 'e7dacb7d-8505-4bf7-abae-91e81d27106e',
-      type: 'type-1',
+      type: 'NodeSwitch',
       x: 300,
       y: 200,
-      w: 240,
-      h: 200,
+    },
+    {
+      id: '329dd688-461d-4dec-af1e-80685b135e27',
+      type: 'NodeFor',
+      x: 600,
+      y: 200,
+    },
+    {
+      id: 'e3c9c844-4333-4d0d-9fad-b952f7f04496',
+      type: 'NodeFor',
+      x: 600,
+      y: 400,
     }
-  ] as Node[],
+  ] as NodeDef[],
   lines: [
-    {source: '8facce0d-c5ff-42ba-b6a0-6a920510c794', target: 'f001c786-c4a0-402f-a79d-19456f9346a6'},
-    {source: '8facce0d-c5ff-42ba-b6a0-6a920510c794', target: 'e7dacb7d-8505-4bf7-abae-91e81d27106e'},
-  ] as Line[]
+    {source: '8facce0d-c5ff-42ba-b6a0-6a920510c794', target: 'f001c786-c4a0-402f-a79d-19456f9346a6', type: 'NODE'},
+    {source: '8facce0d-c5ff-42ba-b6a0-6a920510c794', target: 'e7dacb7d-8505-4bf7-abae-91e81d27106e', type: 'NODE'},
+    {source: 'e7dacb7d-8505-4bf7-abae-91e81d27106e', target: '329dd688-461d-4dec-af1e-80685b135e27', type: 'NODE'},
+    {source: 'b812382b-f1bd-4539-a8a3-0efc875107f9', target: 'e3c9c844-4333-4d0d-9fad-b952f7f04496', type: 'LEVEL'},
+  ] as LineDef[]
 })
 const ref_chart = ref()
 const offsetStyleCpt = computed(() => {
-  let maxW = 0
-  let minW = 0
-  let maxH = 0
-  let minH = 0
-  state.nodes.forEach((v: Node) => {
-    if (v.x < minW) {
-      minW = v.x
+  try {
+    let maxW = 0
+    let minW = 0
+    let maxH = 0
+    let minH = 0
+    const chart = ref_chart.value
+    if (chart) {
+      state.nodes.forEach((v: NodeDef) => {
+        const dom = chart.querySelector(`#NODE_${v.id}`)
+        if (v.x < minW) {
+          minW = v.x
+        }
+        if (v.x + dom.clientWidth > maxW) {
+          maxW = v.x + dom.clientWidth
+        }
+        if (v.y < minH) {
+          minH = v.y
+        }
+        if (v.y + dom.clientHeight > maxH) {
+          maxH = v.y + dom.clientHeight
+        }
+      })
     }
-    if (v.x + v.w > maxW) {
-      maxW = v.x + v.w
+    return {
+      width: maxW - minW - 40,
+      height: maxH - minH,
+      left: minW - 40,
+      top: minH
     }
-    if (v.y < minH) {
-      minH = v.y
-    }
-    if (v.y + v.h > maxH) {
-      maxH = v.y + v.h
-    }
-  })
+  } catch (e) {
+     console.log(e)
+  }
   return {
-    width: maxW - minW - 40,
-    height: maxH - minH,
-    left: minW - 40,
-    top: minH
+    width: 0,
+    height: 0,
+    left: 0,
+    top: 0
   }
 })
 const moveStart = (e) => {
@@ -131,11 +222,11 @@ const moveStart = (e) => {
 }
 const move = (e) => {
   if (state.moving) {
-    const dom = ref_chart.value
-    const x = dom.style.left ? Number(dom.style.left.replace('px', '')) : 0
-    const y = dom.style.top ? Number(dom.style.top.replace('px', '')) : 0
-    dom.style.left = `${x + e.movementX}px`
-    dom.style.top = `${y + e.movementY}px`
+    const chart = ref_chart.value
+    const x = chart.style.left ? Number(chart.style.left.replace('px', '')) : 0
+    const y = chart.style.top ? Number(chart.style.top.replace('px', '')) : 0
+    chart.style.left = `${x + e.movementX}px`
+    chart.style.top = `${y + e.movementY}px`
   }
 }
 const moveEnd = (e) => {
@@ -143,20 +234,31 @@ const moveEnd = (e) => {
 }
 // 计算连线路径
 const getPath = (line) => {
-  const sourceNode = state.nodes.find(node => node.id === line.source);
-  const targetNode = state.nodes.find(node => node.id === line.target);
-  const startX = sourceNode.x + sourceNode.w - offsetStyleCpt.value.left
-  const startY = sourceNode.y + sourceNode.h / 2 - offsetStyleCpt.value.top;
-  const endX = targetNode.x - offsetStyleCpt.value.left - markerStyle.width;
-  const endY = targetNode.y + targetNode.h / 2 - offsetStyleCpt.value.top;
-  // 调整控制点位置,让连线起点和终点保持距离再弯曲
-  const offset = 50;
-  const controlX1 = startX + offset;
-  const controlY1 = startY;
-  const controlX2 = endX - offset;
-  const controlY2 = endY;
-
-  return `M ${startX} ${startY} C ${controlX1} ${controlY1} ${controlX2} ${controlY2} ${endX} ${endY}`;
+  const chart = ref_chart.value
+  if (chart) {
+    const sourceNode = chart.querySelector(`#${line.type}_${line.source}`)
+    const targetNode = chart.querySelector(`#NODE_${line.target}`)
+    let startX
+    let startY
+    if (line.type === 'NODE') {
+      startX = Number(sourceNode.getAttribute('x')) + sourceNode.clientWidth - offsetStyleCpt.value.left + nodeStyleConfig.nodeBorderWidth
+      startY = Number(sourceNode.getAttribute('y')) + nodeStyleConfig.titleHeight / 2 - offsetStyleCpt.value.top + nodeStyleConfig.nodeBorderWidth
+    } else if (line.type === 'LEVEL') {
+      const node = chart.querySelector(`#NODE_${sourceNode.getAttribute('node-id')}`)
+      startX = Number(node.getAttribute('x')) + node.clientWidth - offsetStyleCpt.value.left + nodeStyleConfig.nodeBorderWidth
+      startY = Number(node.getAttribute('y')) + sourceNode.offsetTop + nodeStyleConfig.nodeBorderWidth + nodeStyleConfig.lineOperationRadius + 2
+    }
+    const endX = Number(targetNode.getAttribute('x')) - offsetStyleCpt.value.left - markerStyle.width;
+    const endY = Number(targetNode.getAttribute('y')) - offsetStyleCpt.value.top + nodeStyleConfig.titleHeight / 2 + nodeStyleConfig.nodeBorderWidth
+    // 调整控制点位置,让连线起点和终点保持距离再弯曲
+    const offset = 50
+    const controlX1 = startX + offset
+    const controlY1 = startY
+    const controlX2 = endX - offset
+    const controlY2 = endY
+    return `M ${startX} ${startY} C ${controlX1} ${controlY1} ${controlX2} ${controlY2} ${endX} ${endY}`
+  }
+  return ''
 };
 
 onMounted(() => {
@@ -166,7 +268,7 @@ onMounted(() => {
 <style lang="scss" scoped>
 .chart {
   position: relative;
-  width: 800px;
+  width: 1400px;
   height: 800px;
   overflow: hidden;
   background-color: #f2f4f7;

+ 25 - 0
src/views/draw/config/config.ts

@@ -0,0 +1,25 @@
+import {NodeData} from "./types";
+import {NodeIf} from "@/views/draw/node/component/if/type";
+export const nodeTypeConfig = {
+  NodeRoot: <NodeData>{
+    title: '开始'
+  },
+  NodeIf: <NodeIf>{
+    title: '条件分支',
+    levels: [
+      {}
+    ]
+  },
+  NodeFor: <NodeData>{
+    title: '循环'
+  },
+  NodeSwitch: <NodeData>{
+    title: '分类处理'
+  }
+}
+
+export const nodeStyleConfig = {
+  nodeBorderWidth: 2,
+  titleHeight: 50,
+  lineOperationRadius: 8
+}

+ 15 - 0
src/views/draw/config/types.ts

@@ -0,0 +1,15 @@
+export type NodeDef = {
+  id: string
+  type: string
+  x: number
+  y: number
+  data: NodeData
+}
+export type LineDef = {
+  source: string
+  target: string
+  type: 'NODE' | 'LEVEL'
+}
+export type NodeData = {
+  title: string
+}

+ 47 - 0
src/views/draw/node/component/if/index.vue

@@ -0,0 +1,47 @@
+<template>
+  <div class="node-if">
+    <template v-for="(level, levelIndex) in data.levels">
+      <div class="node-if-level">
+        <div class="node-if-level-title" :node-id="nodeId" :id="'LEVEL_' + level.id">
+          <div>
+            CASE
+          </div>
+          <div>{{levelIndex === 0 ? 'IF' : levelIndex === data.levels.length - 1 ? 'ELSE' : 'ELIF'}}</div>
+        </div>
+        <div class="node-if-level-content">
+
+        </div>
+      </div>
+    </template>
+  </div>
+</template>
+
+<script setup lang="ts">
+import {getCurrentInstance, reactive, ref} from "vue";
+import {NodeIf} from "@/views/draw/node/component/if/type";
+const props = defineProps({
+  data: {default: <NodeIf>{}},
+  nodeId: {}
+})
+const {proxy} = getCurrentInstance()
+const state: any = reactive({
+})
+</script>
+
+<style lang="scss" scoped>
+.node-if {
+  font-size: 14px;
+  display: flex;
+  flex-direction: column;
+  gap: 4px;
+  .node-if-level-title {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    >div:nth-child(1) {
+      font-size: 12px;
+      color: #333333;
+    }
+  }
+}
+</style>

+ 18 - 0
src/views/draw/node/component/if/type.ts

@@ -0,0 +1,18 @@
+import {NodeData} from "@/views/draw/config/types";
+
+export type NodeIf = NodeData & {
+  levels: IfLevel[]
+}
+
+export type IfLevel = {
+  id: string
+  method: 'AND' | 'OR',
+  conditions: IfLevelsCondition[]
+}
+
+export type IfLevelsCondition = {
+  source: string
+  to: string
+  target: string
+  targetType: 'value' | 'param'
+}

+ 85 - 11
src/views/draw/node/index.vue

@@ -1,34 +1,67 @@
 <template>
-  <div class="node" ref="ref_node" :style="{
-    width: w + 'px',
-    height: h + 'px',
-    left: x + 'px',
-    top: y + 'px',
-  }"
+  <div
+    :id="'NODE_' + item.id"
+    class="node"
+    ref="ref_node"
+    :style="{
+      left: x + 'px',
+      top: y + 'px',
+      borderWidth: nodeStyleConfig.nodeBorderWidth + 'px',
+    }"
+    :x="x"
+    :y="y"
     @mousedown.stop="moveStart"
     @mousemove.stop="move"
     @mouseup.stop="moveEnd"
     @mouseleave.stop="moveEnd"
+    @mouseenter="state.hover = true"
   >
-    {{ type }}
+    <div class="node-title" :style="{height: nodeStyleConfig.titleHeight + 'px'}">
+      {{item.data?.title || nodeTypeConfig[type].title}}
+    </div>
+    <div class="node-content" :class="`node-content_${type}`" v-if="type !== 'NodeRoot'">
+      <template v-if="type === 'NodeIf'">
+        <nodeIf :data="item.data" :nodeId="item.id"/>
+      </template>
+    </div>
+    <template v-if="state.active || state.hover">
+      <template v-if="item.data?.levels?.length > 0">
+        <template v-for="level in item.data.levels">
+          <div class="line-operation" :style="{top: getLevelTop(level)}">
+            <SvgIcon name="czr_add" color="#ffffff" size="12"/>
+          </div>
+        </template>
+      </template>
+      <template v-else>
+        <div class="line-operation" :style="{top: (nodeStyleConfig.titleHeight / 2 - addRadius) + 'px'}">
+          <SvgIcon name="czr_add" color="#ffffff" :size="addRadius + 2"/>
+        </div>
+      </template>
+    </template>
   </div>
 </template>
 
 <script setup lang="ts">
 import {getCurrentInstance, reactive, ref} from "vue";
+import {nodeStyleConfig, nodeTypeConfig} from '@/views/draw/config/config'
+import nodeIf from '@/views/draw/node/component/if/index.vue'
+import {IfLevel} from "@/views/draw/node/component/if/type";
 
 const emits = defineEmits(['update:x', 'update:y', 'update:w', 'update:h'])
 const props = defineProps({
+  item: {default: <any>{}},
   type: {default: 'root'},
   x: {default: 0},
   y: {default: 0},
-  w: {default: 100},
-  h: {default: 100},
 })
 const {proxy} = getCurrentInstance()
 const state: any = reactive({
   moving: false,
+  active: false,
+  hover: false,
 })
+const addRadius = nodeStyleConfig.lineOperationRadius
+const addRadiusPx = addRadius + 'px'
 const ref_node = ref()
 const moveStart = (e) => {
   state.moving = true
@@ -41,14 +74,55 @@ const move = (e) => {
 }
 const moveEnd = (e) => {
   state.moving = false
+  state.hover = false
+}
+const getLevelTop = (level: IfLevel) => {
+  let top = 0
+  const node = ref_node.value
+  if (node) {
+    const dom = node.querySelector(`#LEVEL_${level.id}`)
+    top = dom.offsetTop + 2
+  }
+  return top + 'px'
 }
 </script>
 
 <style lang="scss" scoped>
 .node {
-  border: 1px solid gray;
+  z-index: 10;
+  border-style: solid;
+  border-color: rgba(0, 0, 0, 0.1);
   border-radius: 10px;
-  background-color: #409eff;
+  background-color: #ffffff;
   position: absolute;
+  min-width: 240px;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+  &:hover {
+    border-color: #286bfb;
+    cursor: pointer;
+  }
+  .node-title {
+    width: 100%;
+    display: flex;
+    align-items: center;
+    padding: 8px 12px;
+    font-size: 14px;
+    font-weight: bold;
+  }
+  .node-content {
+    padding: 0px 12px;
+    margin-bottom: 8px;
+  }
+  .line-operation {
+    background-color: #286bfb;
+    width: calc(v-bind(addRadiusPx) * 2);
+    height: calc(v-bind(addRadiusPx) * 2);
+    border-radius: 50%;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    position: absolute;
+    right: calc(v-bind(addRadiusPx) * -1);
+  }
 }
 </style>

Datei-Diff unterdrückt, da er zu groß ist
+ 4828 - 0
yarn.lock