diff --git a/cmdb-ui/src/modules/cmdb/views/ci/modules/ciDetailRelationTopo/index.vue b/cmdb-ui/src/modules/cmdb/views/ci/modules/ciDetailRelationTopo/index.vue
index 51d7bb2..679d79a 100644
--- a/cmdb-ui/src/modules/cmdb/views/ci/modules/ciDetailRelationTopo/index.vue
+++ b/cmdb-ui/src/modules/cmdb/views/ci/modules/ciDetailRelationTopo/index.vue
@@ -1,163 +1,168 @@
-<template>
-  <div
-    id="ci-detail-relation-topo"
-    class="ci-detail-relation-topo"
-    :style="{ width: '100%', marginTop: '20px', height: 'calc(100vh - 136px)' }"
-  ></div>
-</template>
-
-<script>
-import _ from 'lodash'
-import { TreeCanvas } from 'butterfly-dag'
-import { searchCIRelation } from '@/modules/cmdb/api/CIRelation'
-import Node from './node.js'
-
-import 'butterfly-dag/dist/index.css'
-import './index.less'
-
-export default {
-  name: 'CiDetailRelationTopo',
-  data() {
-    return {
-      topoData: {},
-    }
-  },
-  inject: ['ci_types'],
-  mounted() {},
-  methods: {
-    init() {
-      const root = document.getElementById('ci-detail-relation-topo')
-      this.canvas = new TreeCanvas({
-        root: root,
-        disLinkable: false, // 可删除连线
-        linkable: false, // 可连线
-        draggable: true, // 可拖动
-        zoomable: true, // 可放大
-        moveable: true, // 可平移
-        theme: {
-          edge: {
-            shapeType: 'AdvancedBezier',
-            arrow: true,
-            arrowPosition: 1,
-          },
-        },
-        layout: {
-          type: 'mindmap',
-          options: {
-            direction: 'H',
-            getSide(d) {
-              return d.data.side || 'right'
-            },
-            getHeight(d) {
-              return 10
-            },
-            getWidth(d) {
-              return 40
-            },
-            getHGap(d) {
-              return 80
-            },
-            getVGap(d) {
-              return 40
-            },
-          },
-        },
-      })
-      this.canvas.setZoomable(true, true)
-      this.canvas.on('events', ({ type, data }) => {
-        const sourceNode = data?.id || null
-        if (type === 'custom:clickLeft') {
-          searchCIRelation(`root_id=${Number(sourceNode)}&&level=1&&reverse=1&&count=10000`).then((res) => {
-            this.redrawData(res, sourceNode, 'left')
-          })
-        }
-        if (type === 'custom:clickRight') {
-          searchCIRelation(`root_id=${Number(sourceNode)}&&level=1&&reverse=0&&count=10000`).then((res) => {
-            this.redrawData(res, sourceNode, 'right')
-          })
-        }
-      })
-    },
-    setTopoData(data) {
-      this.canvas = null
-      this.init()
-      this.topoData = _.cloneDeep(data)
-      this.canvas.draw(data, {}, () => {
-        this.canvas.focusCenterWithAnimate()
-      })
-    },
-    redrawData(res, sourceNode, side) {
-      const newNodes = []
-      const newEdges = []
-      if (!res.result.length) {
-        this.$message.info('无层级关系!')
-        return
-      }
-      const ci_types_list = this.ci_types()
-      res.result.forEach((r) => {
-        const _findCiType = ci_types_list.find((item) => item.id === r._type)
-        newNodes.push({
-          id: `${r._id}`,
-          Class: Node,
-          title: r.ci_type_alias || r.ci_type,
-          name: r.ci_type,
-          side: side,
-          unique_alias: r.unique_alias,
-          unique_name: r.unique,
-          unique_value: r[r.unique],
-          children: [],
-          icon: _findCiType?.icon || '',
-          endpoints: [
-            {
-              id: 'left',
-              orientation: [-1, 0],
-              pos: [0, 0.5],
-            },
-            {
-              id: 'right',
-              orientation: [1, 0],
-              pos: [0, 0.5],
-            },
-          ],
-        })
-        newEdges.push({
-          id: `${r._id}`,
-          source: 'right',
-          target: 'left',
-          sourceNode: side === 'right' ? sourceNode : `${r._id}`,
-          targetNode: side === 'right' ? `${r._id}` : sourceNode,
-          type: 'endpoint',
-        })
-      })
-      const { nodes, edges } = this.canvas.getDataMap()
-      // 删除原节点和边
-      this.canvas.removeNodes(nodes.map((node) => node.id))
-      this.canvas.removeEdges(edges)
-
-      const _topoData = _.cloneDeep(this.topoData)
-      let result
-      const getTreeItem = (data, id) => {
-        for (let i = 0; i < data.length; i++) {
-          if (data[i].id === id) {
-            result = data[i] // 结果赋值
-            break
-          } else {
-            if (data[i].children && data[i].children.length) {
-              getTreeItem(data[i].children, id)
-            }
-          }
-        }
-      }
-
-      getTreeItem(_topoData.nodes.children, sourceNode)
-      result.children.push(...newNodes)
-      _topoData.edges.push(...newEdges)
-
-      this.topoData = _topoData
-      this.canvas.draw(_topoData, {}, () => {})
-    },
-  },
-}
-</script>
-
-<style></style>
+<template>
+  <div
+    id="ci-detail-relation-topo"
+    class="ci-detail-relation-topo"
+    :style="{ width: '100%', marginTop: '20px', height: 'calc(100vh - 136px)' }"
+  ></div>
+</template>
+
+<script>
+import _ from 'lodash'
+import { TreeCanvas } from 'butterfly-dag'
+import { searchCIRelation } from '@/modules/cmdb/api/CIRelation'
+import Node from './node.js'
+
+import 'butterfly-dag/dist/index.css'
+import './index.less'
+
+export default {
+  name: 'CiDetailRelationTopo',
+  data() {
+    return {
+      topoData: {},
+      exsited_ci: [],
+    }
+  },
+  inject: ['ci_types'],
+  mounted() {},
+  methods: {
+    init() {
+      const root = document.getElementById('ci-detail-relation-topo')
+      this.canvas = new TreeCanvas({
+        root: root,
+        disLinkable: false, // 可删除连线
+        linkable: false, // 可连线
+        draggable: true, // 可拖动
+        zoomable: true, // 可放大
+        moveable: true, // 可平移
+        theme: {
+          edge: {
+            shapeType: 'AdvancedBezier',
+            arrow: true,
+            arrowPosition: 1,
+          },
+        },
+        layout: {
+          type: 'mindmap',
+          options: {
+            direction: 'H',
+            getSide(d) {
+              return d.data.side || 'right'
+            },
+            getHeight(d) {
+              return 10
+            },
+            getWidth(d) {
+              return 40
+            },
+            getHGap(d) {
+              return 80
+            },
+            getVGap(d) {
+              return 40
+            },
+          },
+        },
+      })
+      this.canvas.setZoomable(true, true)
+      this.canvas.on('events', ({ type, data }) => {
+        const sourceNode = data?.id || null
+        if (type === 'custom:clickLeft') {
+          searchCIRelation(`root_id=${Number(sourceNode)}&&level=1&&reverse=1&&count=10000`).then((res) => {
+            this.redrawData(res, sourceNode, 'left')
+          })
+        }
+        if (type === 'custom:clickRight') {
+          searchCIRelation(`root_id=${Number(sourceNode)}&&level=1&&reverse=0&&count=10000`).then((res) => {
+            this.redrawData(res, sourceNode, 'right')
+          })
+        }
+      })
+    },
+    setTopoData(data) {
+      this.canvas = null
+      this.init()
+      this.topoData = _.cloneDeep(data)
+      this.canvas.draw(data, {}, () => {
+        this.canvas.focusCenterWithAnimate()
+      })
+    },
+    redrawData(res, sourceNode, side) {
+      const newNodes = []
+      const newEdges = []
+      if (!res.result.length) {
+        this.$message.info('无层级关系!')
+        return
+      }
+      const ci_types_list = this.ci_types()
+      res.result.forEach((r) => {
+        if (!this.exsited_ci.includes(r._id)) {
+          const _findCiType = ci_types_list.find((item) => item.id === r._type)
+          newNodes.push({
+            id: `${r._id}`,
+            Class: Node,
+            title: r.ci_type_alias || r.ci_type,
+            name: r.ci_type,
+            side: side,
+            unique_alias: r.unique_alias,
+            unique_name: r.unique,
+            unique_value: r[r.unique],
+            children: [],
+            icon: _findCiType?.icon || '',
+            endpoints: [
+              {
+                id: 'left',
+                orientation: [-1, 0],
+                pos: [0, 0.5],
+              },
+              {
+                id: 'right',
+                orientation: [1, 0],
+                pos: [0, 0.5],
+              },
+            ],
+          })
+        }
+        newEdges.push({
+          id: `${r._id}`,
+          source: 'right',
+          target: 'left',
+          sourceNode: side === 'right' ? sourceNode : `${r._id}`,
+          targetNode: side === 'right' ? `${r._id}` : sourceNode,
+          type: 'endpoint',
+        })
+      })
+      const { nodes, edges } = this.canvas.getDataMap()
+      // 删除原节点和边
+      this.canvas.removeNodes(nodes.map((node) => node.id))
+      this.canvas.removeEdges(edges)
+
+      const _topoData = _.cloneDeep(this.topoData)
+      _topoData.edges.push(...newEdges)
+      let result
+      const getTreeItem = (data, id) => {
+        for (let i = 0; i < data.length; i++) {
+          if (data[i].id === id) {
+            result = data[i] // 结果赋值
+            result.edges = _topoData.edges
+            break
+          } else {
+            if (data[i].children && data[i].children.length) {
+              getTreeItem(data[i].children, id)
+            }
+          }
+        }
+      }
+
+      getTreeItem(_topoData.nodes.children, sourceNode)
+      result.children.push(...newNodes)
+
+      this.topoData = _topoData
+      this.canvas.draw(_topoData, {}, () => {})
+      this.exsited_ci = [...new Set([...this.exsited_ci, ...res.result.map((r) => r._id)])]
+    },
+  },
+}
+</script>
+
+<style></style>
diff --git a/cmdb-ui/src/modules/cmdb/views/ci/modules/ciDetailRelationTopo/node.js b/cmdb-ui/src/modules/cmdb/views/ci/modules/ciDetailRelationTopo/node.js
index b2b4143..af8cd49 100644
--- a/cmdb-ui/src/modules/cmdb/views/ci/modules/ciDetailRelationTopo/node.js
+++ b/cmdb-ui/src/modules/cmdb/views/ci/modules/ciDetailRelationTopo/node.js
@@ -1,56 +1,56 @@
-/* eslint-disable no-useless-constructor */
-import { TreeNode } from 'butterfly-dag'
-
-import $ from 'jquery'
-
-class BaseNode extends TreeNode {
-    constructor(opts) {
-        super(opts)
-    }
-
-    draw = (opts) => {
-        const container = $(`<div class="${opts.id.startsWith('Root') ? 'root' : ''} ci-detail-relation-topo-node"></div>`)
-            .css('top', opts.top)
-            .css('left', opts.left)
-            .attr('id', opts.id)
-        let icon
-        if (opts.options.icon) {
-            if (opts.options.icon.split('$$')[2]) {
-                icon = $(`<img style="max-width:16px;max-height:16px;" src="/api/common-setting/v1/file/${opts.options.icon.split('$$')[3]}" />`)
-            } else {
-                icon = $(`<svg class="icon" style="color:${opts.options.icon.split('$$')[1]}" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><use data-v-5bd421da="" xlink:href="#${opts.options.icon.split('$$')[0]}"></use></svg>`)
-            }
-        } else {
-            icon = $(`<span class="icon icon-default">${opts.options.name[0].toUpperCase()}</span>`)
-        }
-
-        const titleContent = $(`<div title=${opts.options.title} class="title">${opts.options.title}</div>`)
-        const uniqueDom = $(`<div class="unique">${opts.options.unique_alias || opts.options.unique_name}:${opts.options.unique_value}<div>`)
-        container.append(icon)
-        container.append(titleContent)
-        container.append(uniqueDom)
-
-        if (opts.options.side && !opts.options.children.length) {
-            const addIcon = $(`<i aria-label="图标: plus-square" class="anticon anticon-plus-square add-icon-${opts.options.side}"><svg viewBox="64 64 896 896" data-icon="plus-square" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M328 544h152v152c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8V544h152c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8H544V328c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8v152H328c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8z"></path><path d="M880 112H144c-17.7 0-32 14.3-32 32v736c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V144c0-17.7-14.3-32-32-32zm-40 728H184V184h656v656z"></path></svg></i>`)
-            container.append(addIcon)
-            addIcon.on('click', () => {
-                if (opts.options.side === 'left') {
-                    this.emit('events', {
-                        type: 'custom:clickLeft',
-                        data: { ...this }
-                    })
-                }
-                if (opts.options.side === 'right') {
-                    this.emit('events', {
-                        type: 'custom:clickRight',
-                        data: { ...this }
-                    })
-                }
-            })
-        }
-
-        return container[0]
-    }
-}
-
-export default BaseNode
+/* eslint-disable no-useless-constructor */
+import { TreeNode } from 'butterfly-dag'
+
+import $ from 'jquery'
+
+class BaseNode extends TreeNode {
+    constructor(opts) {
+        super(opts)
+    }
+
+    draw = (opts) => {
+        const container = $(`<div class="${opts.id.startsWith('Root') ? 'root' : ''} ci-detail-relation-topo-node"></div>`)
+            .css('top', opts.top)
+            .css('left', opts.left)
+            .attr('id', opts.id)
+        let icon
+        if (opts.options.icon) {
+            if (opts.options.icon.split('$$')[2]) {
+                icon = $(`<img style="max-width:16px;max-height:16px;" src="/api/common-setting/v1/file/${opts.options.icon.split('$$')[3]}" />`)
+            } else {
+                icon = $(`<svg class="icon" style="color:${opts.options.icon.split('$$')[1]}" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><use data-v-5bd421da="" xlink:href="#${opts.options.icon.split('$$')[0]}"></use></svg>`)
+            }
+        } else {
+            icon = $(`<span class="icon icon-default">${opts.options.name[0].toUpperCase()}</span>`)
+        }
+
+        const titleContent = $(`<div title=${opts.options.title} class="title">${opts.options.title}</div>`)
+        const uniqueDom = $(`<div class="unique">${opts.options.unique_alias || opts.options.unique_name}:${opts.options.unique_value}<div>`)
+        container.append(icon)
+        container.append(titleContent)
+        container.append(uniqueDom)
+
+        if (opts.options.side && (!opts.options.children.length && !(opts.options.edges && opts.options.edges.length && opts.options.edges.find(e => e.source === opts.options.side && e.sourceNode === opts.options.id)))) {
+            const addIcon = $(`<i aria-label="图标: plus-square" class="anticon anticon-plus-square add-icon-${opts.options.side}"><svg viewBox="64 64 896 896" data-icon="plus-square" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M328 544h152v152c0 4.4 3.6 8 8 8h48c4.4 0 8-3.6 8-8V544h152c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8H544V328c0-4.4-3.6-8-8-8h-48c-4.4 0-8 3.6-8 8v152H328c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8z"></path><path d="M880 112H144c-17.7 0-32 14.3-32 32v736c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V144c0-17.7-14.3-32-32-32zm-40 728H184V184h656v656z"></path></svg></i>`)
+            container.append(addIcon)
+            addIcon.on('click', () => {
+                if (opts.options.side === 'left') {
+                    this.emit('events', {
+                        type: 'custom:clickLeft',
+                        data: { ...this }
+                    })
+                }
+                if (opts.options.side === 'right') {
+                    this.emit('events', {
+                        type: 'custom:clickRight',
+                        data: { ...this }
+                    })
+                }
+            })
+        }
+
+        return container[0]
+    }
+}
+
+export default BaseNode