Ver Fonte

输入输出控制块--未写完,虚端子关系,定值条目

“yueshang” há 1 ano atrás
pai
commit
335b146815

+ 17 - 1
src/api/iedNetwork/index.js

@@ -106,6 +106,22 @@ export function gooseReceivefcdalist(query) {
     params: query
   })
 }
-
+//虚端子关系
+//中间列表数据
+export function getMiddleinputs(query) {
+  return request({
+    url: 'screen/scd/ied/inputs',
+    method: 'get',
+    params: query
+  })
+}
+//定值条目
+export function getDingzhis(query) {
+  return request({
+    url: 'screen/scd/ied/dingzhi',
+    method: 'get',
+    params: query
+  })
+}
 
 

+ 49 - 33
src/pages/mission/components/NoMisBox.vue

@@ -1,6 +1,11 @@
 <template>
     <div>
         <div class="noMisBox" v-if="selects == 0 && arrList.length == 0">
+            <div class="settingBox">
+                <div class="setNow">
+              <span class="setnowspan" @click="goNetStructPicture">scd可视化</span>
+            </div>
+              </div>
             <img style="width: 220px;height: 220px;" src="../../../assets/image/create.png" alt="">
             <p style="text-align: center;width: 240px;">当前没有需要检测的任务</p>
             <p class="createMisp" @click="createMis">
@@ -12,46 +17,57 @@
 </template>
 
 <script>
-import { ref, onMounted } from 'vue';
+import { ref, onMounted } from "vue";
+import { useRouter } from "vue-router";
 export default {
-    props: {
-        btnSelect: {
-            type: Number,
-            require: true
-        },
-        taskList: {
-            type: Array,
-            require: true
-        }
+  props: {
+    btnSelect: {
+      type: Number,
+      require: true,
     },
-    setup(props, { emit }) {
-        let selects = props.btnSelect
-        let arrList = props.taskList
-        function createMis() {
-            selects = 1
-            emit("backToMission", selects)
-        }
-
-        return {
-            createMis,
-            selects,
-            arrList,
-        }
+    taskList: {
+      type: Array,
+      require: true,
+    },
+  },
+  setup(props, { emit }) {
+    let router = useRouter();
+    let selects = props.btnSelect;
+    let arrList = props.taskList;
+    function createMis() {
+      selects = 1;
+      emit("backToMission", selects);
     }
-}
+    function goNetStructPicture() {
+      router.push({
+        path: "/home/netStructPicture",
+        query: {
+          id: 4000002,
+          name: "UnitTest_检测任务",
+        },
+      });
+    }
+    return {
+      createMis,
+      selects,
+      arrList,
+      goNetStructPicture,
+    };
+  },
+};
 </script>
 
 <style scoped>
 .noMisBox {
-    width: 200px;
-    height: 200px;
-    margin: 80px auto;
-    text-align: center;
-    line-height: 20px;
+  width: 200px;
+  height: 200px;
+  margin: 80px auto;
+  text-align: center;
+  line-height: 20px;
 }
-.createMisp{
-    margin-left: 25px;
-    text-align: center;
-    line-height: 10px;
+.createMisp {
+  margin-left: 25px;
+  text-align: center;
+  line-height: 10px;
 }
 </style>

+ 9 - 1
src/pages/netStructPicture/components/dialogIndex.vue

@@ -76,6 +76,8 @@
             :iedRelation="iedRelation"
             @result="result"
             :isPhoto="isPhoto"
+            :tabName="tabName"
+            ref="getChild"
           ></component>
         </div>
       </div>
@@ -185,11 +187,13 @@ const cancelClick = () => {
 const activeNav = ref(0);
 const activeNavName = shallowRef(relationShip);
 const inoutName = ref("");
+const tabName = ref('relationShip');
 const clickNav = (navIndex, name) => {
   //点击导航栏事件
   inoutName.value = name;
   console.log("name", name);
   activeNavName.value = name;
+  tabName.value = name;
   activeNav.value = navIndex;
 };
 const activeLeft = ref(null);
@@ -209,6 +213,10 @@ const clickInoutNav = (item, index) => {
   isPhoto.value = item.code;
   inoutItemNavIndex.value = index;
 };
+const getChild = ref(null);
+watch(()=>tabName.value,(newValue)=>{
+  getChild.value.removeLine;
+})
 </script>
   <style scoped lang="scss">
 @mixin mid-center {
@@ -307,6 +315,6 @@ $height: 40px;
 .inout-right-item{
   margin-top: 27px;
   margin-left: 16px;
-  overflow-y: hidden !important;
+  // overflow-y: hidden !important;
 }
 </style>

+ 103 - 4
src/pages/netStructPicture/components/fixedEntry.vue

@@ -1,15 +1,114 @@
-<!-- 定值条目 -->
+<!-- 基础信息 -->
 <template>
-  <div>5</div>
+  <div>
+    <div class="cont-table">
+      <el-table
+        :data="dingzhiData"
+        stripe
+        style="width: 100%"
+        :cell-style="{ color: '#000',cursor:'pointer' }"
+        @row-click="svSendRowClick3"
+        :highlight-current-row="true"
+        ref="myTable5"
+      >
+        <el-table-column type="index" label="序号" width="80" />
+        <el-table-column prop="accesspoint_name" label="访问点" width="180" />
+        <el-table-column label="数据集名称">
+          <template #default="scope">
+            {{ `${scope.row.ld_desc}(${scope.row.ld_name})` }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="dataset_desc" label="数据集描述" />
+      </el-table>
+      <div class="title">
+        定值条目列表详情(共<span v-if="gooseList">{{ gooseList.length }}</span
+        >条)
+      </div>
+      <el-table
+        :data="dingzhiDataChild"
+        style="width: 100%"
+        stripe
+        :cell-style="{ color: '#000' }"
+      >
+        <el-table-column type="index" label="序号" width="80" />
+        <el-table-column prop="doi_desc" label="条目描述" width="180" />
+        <el-table-column label="最大值" prop="da_maxval"/>
+        <el-table-column label="最小值" prop="da_minval"/>
+        <el-table-column prop="da_units" label="单位" />
+        <el-table-column prop="da_stepsize" label="步长" />
+        <el-table-column prop="da_datatype" label="数据类型" />
+        <el-table-column prop="short_addr" label="内部地址" />
+      </el-table>
+    </div>
+  </div>
 </template>
 <script setup>
-import { onMounted, watch, ref,nextTick,defineEmits } from "vue";
+import { onMounted, watch, ref, nextTick, defineEmits, inject } from "vue";
+import {
+  //send发送
+  gooseSendfcdalist,
+  iedSVSendCtrlblock,
+  getDingzhis
+} from "@/api/iedNetwork";
 const props = defineProps({
-  checkDialogData: {
+  checkData: {
     type: Object,
     default: () => {},
   },
+  isPhoto: {
+    type: String,
+    default: "",
+  },
+  svInfo: {
+    type: Array,
+    default: [],
+  },
+});
+const scdIdValue = inject("scdId");
+const dingzhiData = ref(null);
+const dingzhiDataChild = ref(null);
+const myTable5 = ref(null);
+const getDingzhisList = async () => {
+  const dingRes= await getDingzhis({
+    scd_id: scdIdValue,
+    ied_name: props.checkData.ied_name,
+  })
+  if(dingRes.data.length>0){
+    dingzhiData.value = dingRes.data;
+    dingzhiDataChild.value = dingRes.data[0].list
+  }
+
+  myTable5.value.setCurrentRow(dingzhiData.value[0]);
+}
+const svSendRowClick3 = (row, column) => {
+  dingzhiDataChild.value = row.list
+}
+watch(
+  () => props.checkData,
+  (newValue) => {
+    svTableCtrl.value = [];
+    gooseList.value = [];
+    if (newValue != null) {
+      getDingzhisList()
+    }
+  }
+);
+onMounted(() => {
+  getDingzhisList()
 });
 </script>
 <style scoped lang="scss">
+.cont-table {
+  height: 65vh;
+  display: flex;
+  flex-direction: column;
+  justify-content: space-between;
+}
+:deep(.el-table) {
+  flex-basis: 45%;
+}
+.title {
+  margin: 16px;
+  color: #51637f;
+}
 </style>

+ 269 - 192
src/pages/netStructPicture/components/inoutControl.vue

@@ -3,18 +3,18 @@
   <div>
     <!-- 关联图 -->
     <div
-      v-if="isPhoto == 'photo'"
+      v-if="tabValue == 'photo'"
       class="main-cont"
       ref="myElement"
-      id="treedom"
+      id="treedom2"
     >
-      <div class="main-left">
+      <div class="main-left" ref="leftElement">
         <div
           v-for="(item, index) in leftList"
           :key="index"
           class="conts"
           @click="clickImg(item)"
-          :ref="(el) => setdom(el, item)"
+          :ref="(el) => setdom2(el, item)"
         >
           <div class="cont-item">
             <div>{{ item.ref_ied_name }}</div>
@@ -28,6 +28,7 @@
               class="ied-desc-child"
               v-for="(item3, index3) in cItem.childItem"
               :key="index3"
+              :ref="(el) => setdomLeftChild2(el, item3)"
             >
               <div>{{ item3.ctrl_name }}</div>
               <div>{{ item3.datset_desc }}</div>
@@ -36,31 +37,32 @@
         </div>
       </div>
       <!-- 中间部分 -->
-      <div class="main-middle" ref="middleHeight">
+      <div class="main-middle" ref="middleHeight" id="end">
         <div class="middle-title">
-          <div v-if="listData">{{ listData.ied_name }}</div>
-          <div v-if="listData">{{ listData.desc }}</div>
+          <div v-if="listData2">{{ listData2.ied_name }}</div>
+          <div v-if="listData2">{{ listData2.desc }}</div>
         </div>
         <div class="middle-item" ref="middleElement">
           <div
-            class="ied-desc-child middle-child"
-            v-for="(itemChild, index) in svInfo"
+            class="midlestyle"
+            v-for="(item, index) in svInfo"
             :key="index"
+            :ref="(el) => setdomMiddle2(el, item)"
           >
-            <div>{{ `${itemChild.ldinst}/${itemChild.attr_name}` }}</div>
-            <div>{{ itemChild.datset_desc }}</div>
-            <div class="ied-desc">APPID:{{ itemChild.APPID }}</div>
+            <div>{{ `${item.ldinst}/${item.attr_name}` }}</div>
+            <div>{{ item.datset_desc }}</div>
+            <div class="ied-desc">APPID:{{ item.APPID }}</div>
           </div>
         </div>
       </div>
       <!-- 右侧 -->
-      <div class="main-right">
+      <div class="main-right" ref="rightElement">
         <div
           v-for="(item, index) in rightList"
           :key="index"
           class="conts"
           @click="clickImg(item)"
-          :ref="(el) => setdomRight(el, item)"
+          :ref="(el) => setdomRight2(el, item)"
         >
           <div class="cont-item">
             <div>{{ item.ref_ied_name }}</div>
@@ -74,6 +76,7 @@
               class="ied-desc-child"
               v-for="(item3, index3) in cItem.childItem"
               :key="index3"
+              :ref="(el) => setdomRight2Child2(el, item3)"
             >
               <div>{{ item3.ctrl_name }}</div>
               <div>{{ item3.datset_desc }}</div>
@@ -83,7 +86,7 @@
       </div>
       <div id="wrapper"></div>
     </div>
-    <div v-if="isPhoto != 'photo'">
+    <div v-if="tabValue != 'photo'">
       <inoutSendOrRv
         :isPhoto="isPhoto"
         :checkData="checkData"
@@ -93,7 +96,7 @@
   </div>
 </template>
 <script setup>
-import { onMounted, watch, ref, nextTick, defineEmits, inject } from "vue";
+import { onMounted, watch, ref, nextTick, defineEmits, inject  } from "vue";
 import devicePng from "@/assets/image/instruct/device.png";
 import LeaderLine from "../../../../public/leader-line.min.js";
 import AnimEvent from "../../../../public/anim-event.min.js";
@@ -126,6 +129,10 @@ const props = defineProps({
     type: Object,
     default: () => {},
   },
+  tabName: {
+    type: String,
+    default: "",
+  },
 });
 const svInfo = ref(null);
 const scdIdValue = inject("scdId");
@@ -138,7 +145,7 @@ const getIedChild = async () => {
     forcerefresh: 0,
   });
   //左右侧内部侧数据组装
-  listData.value.list.forEach((item, index) => {
+  listData2.value.list.forEach((item, index) => {
     item.titleItems = [];
     childRes.data.forEach((key) => {
       if (key.target_ied_name === item.ref_ied_name) {
@@ -177,47 +184,66 @@ const getNetworkInfo = async () => {
     scd_id: scdIdValue,
     ied_name: props.checkData.ied_name,
   });
-  if (svResInfoCtrl.data.GSEControl) {
+  if (svResInfoCtrl.data && svResInfoCtrl.data.GSEControl) {
     svResInfo.data.forEach((item) => {
       svResInfoCtrl.data.GSEControl.forEach((key) => {
-        key.APPID =
-          item.cb_name == key.attr_name
-            ? JSON.parse(item.address_json).APPID
-            : "";
+        if (item.cb_name == key.attr_name) {
+          key.APPID = JSON.parse(item.address_json).APPID;
+        }
       });
     });
     svInfo.value = svResInfoCtrl.data.GSEControl;
-  }
-};
-watch(
-  () => props.checkData,
-  (newValue) => {
-    if (newValue != null) {
-      getIedChild();
-      getNetworkInfo();
+    if (svInfo.value && svInfo.value.length > 3) {
+      middleElement.value.style.marginTop = "20px";
+    } else {
+      middleElement.value.style.marginTop = "150px"; // 设置元素的垂直位置
     }
   }
-);
+};
 //线条
 const middleElement = ref(null);
 const myElement = ref(null);
-let leaderLines = ref([]); //控制线条显示
+let leaderLines2 = ref([]); //控制线条显示
 const leftList = ref([]);
 const rightList = ref([]);
-const domList = ref(new Map()); //获取 所有的ref
-const domListRight = ref(new Map()); //获取 所有的ref
-const listData = ref(props.checkData); //线条左右两侧的数据
+const domList2 = ref(new Map()); //获取 所有的ref
+const domListRight2 = ref(new Map()); //获取右侧所有的ref
+const domListMiddle2 = ref(new Map()); //获取中间所有的ref
+const domListRightChild2 = ref(new Map()); //获取右侧所有子的ref
+const domListLeftChild2 = ref(new Map()); //获取中间所有子的ref
+const listData2 = ref(props.checkData); //线条左右两侧的数据
 const emit = defineEmits(["result"]); //如果不加这个再次点击左侧会没有反应
-const setdom = (el, item) => {
+const tabValue = ref(props.isPhoto); //点击tab切换的数据
+const rightElement = ref(null);
+const leftElement = ref(null);
+const setdom2 = (el, item) => {
   //左侧dom
   if (el) {
-    domList.value.set(item, el);
+    domList2.value.set(item, el);
   }
 };
-const setdomRight = (el, item) => {
+const setdomRight2 = (el, item) => {
   //右侧dom
   if (el) {
-    domListRight.value.set(item, el);
+    domListRight2.value.set(item, el);
+  }
+};
+const setdomMiddle2 = (el, item) => {
+  // 中间dom
+  if (el) {
+    domListMiddle2.value.set(item, el);
+  }
+};
+//左侧子Dom
+const setdomLeftChild2 = (el, item) => {
+  if (el) {
+    domListLeftChild2.value.set(item, el);
+  }
+};
+//右侧子Dom
+const setdomRight2Child2 = (el, item) => {
+  if (el) {
+    domListRightChild2.value.set(item, el);
   }
 };
 const processArray = (arr) => {
@@ -242,51 +268,21 @@ const processArray = (arr) => {
 const clickImg = (dataItem) => {
   Object.values(props.iedRelation).find((item) => {
     if (item.ied_name == dataItem.ref_ied_name) {
-      listData.value = item;
+      listData2.value = item;
     }
   });
 };
-watch(
-  () => props.checkData,
-  (newValue, oldV) => {
-    listData.value = [];
-    listData.value = newValue;
-    if (newValue && leaderLines.value.length > 0) {
-      // leaderLines.value.forEach((line) => line.remove()); //清除连线
-      leaderLines.value = [];
-    }
-  },
-  { deep: true }
-);
-watch(
-  () => listData.value,
-  (newValue) => {
-    emit("result", newValue);
-    clickResetLine();
-  }
-);
-watch(
-  () => props.isOpen,
-  (newValue) => {
-    if (newValue) {
-      domList.value.clear();
-      domListRight.value.clear();
-      leaderLines.value = [];
-    }
-    nextTick(() => {
-      middleLinePosition();
-      removeLine();
-    });
-  }
-);
+
 //点击后重置数据和线条
-const clickResetLine = () => {
-  domList.value.clear();
-  domListRight.value.clear();
-  leaderLines.value = [];
-  middleLinePosition();
-  setLine();
-  removeLine();
+const clickResetLine2 = () => {
+  domList2.value.clear();
+  domListRight2.value.clear();
+  domListMiddle2.value.clear();
+  domListLeftChild2.value.clear();
+  domListRightChild2.value.clear();
+  leaderLines2.value = [];
+  setLine2();
+  removeLine2();
 };
 // 将设备列表分成两份
 const bothSide = (data) => {
@@ -297,110 +293,156 @@ const bothSide = (data) => {
   rightList.value = formatArr.splice(0);
 };
 
-const setLeaderline = () => {
+const setLeaderline2 = () => {
   // lineArr.value = [];
   //线条样式
-  const lineStyle0 = {
-    color: "#51637F",
-    size: 2,
-    path: "straight",
-    startPlug: "arrow1",
-    endPlug: "behind",
-    startSocket: "right",
-    endSocket: "left",
-  };
-  const lineStyle1 = {
-    color: "#51637F",
-    size: 2,
-    path: "straight",
-    endPlug: "arrow1",
-    startSocket: "right",
-    endSocket: "left",
-  };
-  const lineStyle2 = {
-    color: "#134BEA",
-    size: 2,
-    path: "straight",
-    startPlug: "arrow1",
-    endPlug: "arrow1",
-    startSocket: "right",
-    endSocket: "left",
-  };
-  const lineStyleRight0 = {
-    color: "#51637F",
-    size: 2,
-    path: "straight",
-    startPlug: "arrow1",
-    endPlug: "behind",
-    startSocket: "left",
-    endSocket: "right",
-  };
-  const lineStyleRight1 = {
-    color: "#51637F",
-    size: 2,
-    path: "straight",
-    endPlug: "arrow1",
-    startSocket: "left",
-    endSocket: "right",
-  };
-  const lineStyleRight2 = {
-    color: "#134BEA",
-    size: 2,
-    path: "straight",
-    startPlug: "arrow1",
-    endPlug: "arrow1",
-    startSocket: "left",
-    endSocket: "right",
-  };
   const startDom = document.getElementById("end");
-  //循环画线
-  for (const [key, value] of domList.value) {
+  //左侧子组件
+  for (const [key, value] of domListLeftChild2.value) {
     const endDom = value;
     let line;
-    if (key.ref_type == 0) {
-      // line = new LeaderLine(endDom, startDom, lineStyle0);
-    } else if (key.ref_type == 2) {
-      // line = new LeaderLine(endDom, startDom, lineStyle0);
+    const options = key.inputs_cnt.toString();
+    const rectEnd = endDom.getBoundingClientRect();
+    if (key.ctrl_type == "smv") {
+      line = new LeaderLine(
+        endDom,
+        LeaderLine.pointAnchor(startDom, { x: 0, y: rectEnd.top - 222 }),
+        {
+          color: "red",
+          size: 2,
+          path: "straight",
+          startSocket: "right",
+          endSocket: "left",
+          middleLabel: LeaderLine.captionLabel({
+            text: options,
+          }),
+        }
+      );
+    } else {
+      line = new LeaderLine(
+        endDom,
+        LeaderLine.pointAnchor(startDom, { x: 0, y: rectEnd.top - 222 }),
+        {
+          color: "#255CE7",
+          size: 2,
+          path: "straight",
+          startSocket: "right",
+          endSocket: "left",
+          y: 50,
+          middleLabel: LeaderLine.captionLabel({
+            text: options,
+          }),
+        }
+      );
     }
-    //  保存进数组,方便进行遍历删除
-    leaderLines.value.push(line);
+    leaderLines2.value.push(line);
   }
-  //循环画线右侧
-  for (const [key, value] of domListRight.value) {
+  //右侧子组件
+  for (const [key, value] of domListRightChild2.value) {
     const endDom = value;
-    let line2;
-    if (key.ref_type == 0) {
-      // line2 = new LeaderLine(endDom, startDom, lineStyleRight0);
-    } else if (key.ref_type == 2) {
-      // line2 = new LeaderLine(endDom, startDom, lineStyleRight0);
-    }
-    //  保存进数组,方便进行遍历删除
-    leaderLines.value.push(line2);
-  }
-  hiddenLine();
-};
-const middleHeight = ref(null);
-//设置中间盒子的所在位置
-const middleLinePosition = () => {
-  setTimeout(() => {
-    const heights = myElement.value.scrollHeight;
-    if (leftList.value.length > 2 || rightList.value.length > 2) {
-      middleElement.value.style.marginTop = `${(heights - 60) / 2}px`; // 设置元素的垂直位置
-      middleHeight.value.style.height = heights
+    let line;
+    const rectEnd = endDom.getBoundingClientRect();
+    const options = key.inputs_cnt.toString();
+    if (key.ctrl_type == "smv") {
+      line = new LeaderLine(
+        LeaderLine.pointAnchor(startDom, { x: "100%", y: rectEnd.top - 222 }),
+        endDom,
+        {
+          color: "red",
+          size: 2,
+          path: "straight",
+          startSocket: "right",
+          endSocket: "left",
+          startPlug: "arrow1",
+          endPlug: "behind",
+          middleLabel: LeaderLine.captionLabel(options),
+        }
+      );
     } else {
-      middleElement.value.style.marginTop = "150px"; // 设置元素的垂直位置
+      line = new LeaderLine(
+        LeaderLine.pointAnchor(startDom, { x: "100%", y: rectEnd.top - 222 }),
+        endDom,
+        {
+          color: "#255CE7",
+          size: 2,
+          path: "straight",
+          startSocket: "right",
+          endSocket: "left",
+          startPlug: "arrow1",
+          endPlug: "behind",
+          middleLabel: LeaderLine.captionLabel(options),
+        }
+      );
     }
-  }, 0);
+    leaderLines2.value.push(line);
+  }
+  if (iedChild.value) {
+    iedChild.value.forEach((item) => {
+      //循环画线左侧
+      for (const [key, value] of domList2.value) {
+        let line;
+        for (let [key2, value2] of domListMiddle2.value) {
+          //中间的数据dom
+          if (key.ref_ied_name == item.source_ied_name) {
+            const endDom = value;
+            if (item.ctrl_id == key2.node_id) {
+              const startDom = value2;
+              const options2 = item.inputs_cnt.toString();
+              line = new LeaderLine(endDom, startDom, {
+                color: "#51637F",
+                size: 2,
+                path: "straight",
+                endPlug: "behind",
+                startPlug: "arrow1",
+                startSocket: "right",
+                endSocket: "left",
+                middleLabel: LeaderLine.captionLabel(options2),
+              });
+              leaderLines2.value.push(line);
+            }
+          }
+        }
+      }
+      //循环画线右侧
+      for (let [key, value] of domListRight2.value) {
+        let line2;
+        for (let [key2, value2] of domListMiddle2.value) {
+          //中间的数据dom
+          if (key.ref_ied_name == item.source_ied_name) {
+            const endDom = value;
+            if (item.ctrl_id == key2.node_id) {
+              const startDom = value2;
+              const options3 = item.inputs_cnt.toString();
+              line2 = new LeaderLine(startDom, endDom, {
+                color: "#51637F",
+                size: 2,
+                path: "straight",
+                startPlug: "behind",
+                endPlug: "arrow1",
+                endSocket: "left",
+                startSocket: "right",
+                middleLabel: LeaderLine.captionLabel(options3),
+              });
+              leaderLines2.value.push(line2);
+            }
+          }
+        }
+      }
+    });
+  }
+  hiddenLine2();
 };
 //滚动时重定位线条
-const newPositionLine = () => {
-  document.getElementById("treedom").addEventListener(
+const newPositionLine2 = () => {
+  document.getElementById("treedom2").addEventListener(
     "scroll",
     AnimEvent.add(() => {
-      leaderLines.value.forEach((line) => {
-        hiddenLine();
-        // line.position();
-        // line.positionByWindowResize = false;
+      leaderLines2.value.forEach((line) => {
+        if (line) {
+          hiddenLine2();
+          line.position();
+          line.positionByWindowResize = false;
+        }
       });
       //中间展示图片的
     }),
@@ -408,7 +450,7 @@ const newPositionLine = () => {
   );
 };
 //弹窗打开后使得线条在指定区域中
-const hiddenLine = () => {
+const hiddenLine2 = () => {
   const elmWrapper = document.getElementById("wrapper");
   // 移动 line
   document.body.querySelectorAll("body .leader-line").forEach((node) => {
@@ -421,15 +463,16 @@ const hiddenLine = () => {
     (rectWrapper.left + window.scrollY) * -1
   }px, ${(rectWrapper.top + window.scrollX) * -1}px)`;
 };
-const setLine = () => {
-  if (listData.value) {
-    bothSide(listData.value.list);
+const setLine2 = () => {
+  if (listData2.value) {
+    bothSide(listData2.value.list);
   }
   setTimeout(() => {
-    setLeaderline();
-  }, 30);
+    setLeaderline2();
+  }, 500);
 };
-const removeLine = () => {
+
+const removeLine2 = () => {
   const elmWrapper = document.getElementById("wrapper");
   document.body.querySelectorAll("body .leader-line").forEach((node) => {
     elmWrapper.removeChild(node);
@@ -438,33 +481,68 @@ const removeLine = () => {
 onMounted(() => {
   if (props.checkData != null) {
     getIedChild();
-    getNetworkInfo();
+
   }
   //不加条件切换下方tab时会出现bug
   if (props.isPhoto == "photo") {
+    getNetworkInfo();
     nextTick(() => {
-      setLine();
-      middleLinePosition();
+      setLine2();
       nextTick(() => {
-        newPositionLine();
+        newPositionLine2();
       });
     });
   }
 });
+
 watch(
   () => props.isPhoto,
   (newValue) => {
-    if (props.isPhoto == "photo") {
+    tabValue.value = newValue;
+    if (tabValue.value == "photo") {
       nextTick(() => {
-        setLine();
-        middleLinePosition();
-        nextTick(() => {
-          newPositionLine();
-        });
+        setLine2();
+        newPositionLine2();
       });
     }
   }
 );
+watch(
+  () => props.checkData,
+  (newValue) => {
+    listData2.value = [];
+    svInfo.value = [];
+    listData2.value = newValue;
+    if (newValue != null) {
+      getIedChild();
+   
+    }
+    if (tabValue.value == "photo") {
+      clickResetLine2();
+      getNetworkInfo();
+    }
+    emit("result", newValue);
+    if (newValue && leaderLines2.value.length > 0) {
+      leaderLines2.value = [];
+    }
+  }
+);
+watch(
+  () => props.isOpen,
+  (newValue) => {
+    if (newValue) {
+      domList2.value.clear();
+      domListRight2.value.clear();
+      domListMiddle2.value.clear();
+      domListLeftChild2.value.clear();
+      domListRightChild2.value.clear();
+      leaderLines2.value = [];
+    }
+    nextTick(() => {
+      removeLine2();
+    });
+  }
+);
 </script>
 <style lang="scss"  scoped>
 @mixin img-size {
@@ -479,8 +557,6 @@ watch(
 .main-cont {
   display: flex;
   justify-content: space-evenly;
-  overflow-y: auto;
-  height: 65vh;
 }
 
 .leader-line {
@@ -493,6 +569,7 @@ watch(
 .main-middle {
   box-sizing: border-box;
   border: 2px dashed #98a8ff;
+  background: #edf3ff;
   img {
     margin-bottom: 10px;
   }
@@ -507,8 +584,6 @@ watch(
     color: #ffcb11;
     border-bottom: 1px solid #a3ade0;
   }
-  .middel-child {
-  }
 }
 .main-right {
   display: flex;
@@ -523,6 +598,7 @@ watch(
   margin-bottom: 24px;
   border: 2px dashed #98a8ff;
   cursor: pointer;
+  background: #f7f8fb;
   .cont-item {
     @include left-and-right;
     color: #1a2447;
@@ -543,7 +619,8 @@ watch(
     display: block;
   }
 }
-.ied-desc-child {
+.ied-desc-child,
+.midlestyle {
   @include left-and-right;
   align-items: center;
   border: 1px solid #7484ab;

+ 4 - 4
src/pages/netStructPicture/components/inoutSendOrRv.vue

@@ -6,7 +6,7 @@
         :data="svTableCtrl"
         stripe
         style="width: 100%"
-        :cell-style="{ color: '#000' }"
+        :cell-style="{ color: '#000',cursor:'pointer' }"
         @row-click="svSendRowClick"
         :highlight-current-row="true"
         ref="myTable"
@@ -54,7 +54,7 @@
         :data="svReceive"
         stripe
         style="width: 100%"
-        :cell-style="{ color: '#000' }"
+        :cell-style="{ color: '#000',cursor:'pointer' }"
         @row-click="svSendRowClick2"
         :highlight-current-row="true"
         ref="myTable2"
@@ -105,7 +105,7 @@
         :data="gooseCtrl"
         stripe
         style="width: 100%"
-        :cell-style="{ color: '#000' }"
+        :cell-style="{ color: '#000',cursor:'pointer' }"
         @row-click="svSendRowClick3"
         :highlight-current-row="true"
         ref="myTable3"
@@ -153,7 +153,7 @@
         :data="gooseReCtrl"
         stripe
         style="width: 100%"
-        :cell-style="{ color: '#000' }"
+        :cell-style="{ color: '#000',cursor:'pointer' }"
         @row-click="svSendRowClick4"
         :highlight-current-row="true"
         ref="myTable4"

+ 5 - 5
src/pages/netStructPicture/components/netWork.vue

@@ -151,7 +151,7 @@ const loading = ref(true);
 //获取网络图的顶部列表
 const getNetWork = async () => {
   const infoRes = await nodeList({
-    scd_id: 296000081,
+    scd_id: 452000123,
     pagesize: 10000,
     name: "SubNetwork",
   });
@@ -170,7 +170,7 @@ const allApData = ref({});
 const initLoad = async () => {
   allApData.value = {};
   const allAP = await nodeList({
-    scd_id: 296000081,
+    scd_id: 452000123,
     pagesize: 10000,
     name: "ConnectedAP",
   });
@@ -198,7 +198,7 @@ const initLoad = async () => {
 };
 //处理重复的ip
 const ipNetaddr = async () => {
-  const ipRes = await iedNetaddr({ scd_id: 296000081 });
+  const ipRes = await iedNetaddr({ scd_id: 452000123 });
   if (ipRes.code == 1) {
     return;
   }
@@ -500,11 +500,11 @@ const clickNetworkInfo = (value) => {
 
 const iedRelationData = ref([]);
 const iedRelation = async () => {
-  const iedRes = await scdIedRelation({ scd_id: 296000081 });
+  const iedRes = await scdIedRelation({ scd_id: 452000123 });
   iedRelationData.value = iedRes.data;
 };
 //弹窗=============
-provide('scdId',296000081)
+provide('scdId',452000123)
 </script>
   
 <style scoped lang="scss">

+ 349 - 345
src/pages/netStructPicture/components/relationShip.vue

@@ -1,374 +1,378 @@
 <!-- 装置关联关系 -->
 <template>
-<div>
-  <div class="main-cont" ref="myElement" id="treedom">
-    <div class="main-left">
-      <div
-        v-for="(item, index) in leftList"
-        :key="index"
-        class="cont"
-        @click="clickImg(item)"
-      >
-        <img
-          :src="devicePng"
-          alt=""
-          class="img-item"
-          :ref="(el) => setdom(el, item)"
-        />
-        <div>{{ item.ref_ied_name }}</div>
-        <div class="ied-desc">{{ item.ref_ied_desc }}</div>
+  <div>
+    <div class="main-cont" ref="myElement" id="treedom">
+      <div class="main-left">
+        <div
+          v-for="(item, index) in leftList"
+          :key="index"
+          class="cont"
+          @click="clickImg(item)"
+        >
+          <img
+            :src="devicePng"
+            alt=""
+            class="img-item"
+            :ref="(el) => setdom(el, item)"
+          />
+          <div>{{ item.ref_ied_name }}</div>
+          <div class="ied-desc">{{ item.ref_ied_desc }}</div>
+        </div>
       </div>
-    </div>
-    <div class="main-middle">
-      <div class="middle-item" ref="middleElement">
-        <img :src="devicePng" alt="" id="end" />
-        <div v-if="listData">{{ listData.ied_name }}</div>
-        <div v-if="listData">{{ listData.desc }}</div>
+      <div class="main-middle">
+        <div class="middle-item" ref="middleElement">
+          <img :src="devicePng" alt="" id="end" />
+          <div v-if="listData">{{ listData.ied_name }}</div>
+          <div v-if="listData">{{ listData.desc }}</div>
+        </div>
       </div>
-    </div>
-    <div class="main-right">
-      <div
-        v-for="(item, index) in rightList"
-        :key="index"
-        class="cont"
-        @click="clickImg(item)"
-      >
-        <img
-          :src="devicePng"
-          alt=""
-          class="img-item"
-          :ref="(el) => setdomRight(el, item)"
-        />
-        <div>{{ item.ref_ied_name }}</div>
-        <div class="ied-desc">{{ item.ref_ied_desc }}</div>
+      <div class="main-right">
+        <div
+          v-for="(item, index) in rightList"
+          :key="index"
+          class="cont"
+          @click="clickImg(item)"
+        >
+          <img
+            :src="devicePng"
+            alt=""
+            class="img-item"
+            :ref="(el) => setdomRight(el, item)"
+          />
+          <div>{{ item.ref_ied_name }}</div>
+          <div class="ied-desc">{{ item.ref_ied_desc }}</div>
+        </div>
       </div>
     </div>
+    <div id="wrapper"></div>
   </div>
-  <div id="wrapper"></div>
-</div>
-</template>
-<script setup>
-import { onMounted, watch, ref, nextTick, defineEmits } from "vue";
-import devicePng from "@/assets/image/instruct/device.png";
-import LeaderLine from "../../../../public/leader-line.min.js";
-import AnimEvent from "../../../../public/anim-event.min.js";
-const props = defineProps({
-  checkData: {
-    type: Object,
-    default: () => {},
-  },
-  isOpen: {
-    type: Boolean,
-    default: false,
-  },
-  iedRelation: {
-    type: Object,
-    default: () => {},
+  </template>
+  <script setup>
+  import { onMounted, watch, ref, nextTick, defineEmits } from "vue";
+  import devicePng from "@/assets/image/instruct/device.png";
+  import LeaderLine from "../../../../public/leader-line.min.js";
+  import AnimEvent from "../../../../public/anim-event.min.js";
+  const props = defineProps({
+    checkData: {
+      type: Object,
+      default: () => {},
+    },
+    isOpen: {
+      type: Boolean,
+      default: false,
+    },
+    iedRelation: {
+      type: Object,
+      default: () => {},
+    },
+    tabName: {
+    type: String,
+    default: "",
   },
-});
-const middleElement = ref(null);
-const myElement = ref(null);
-let leaderLines = ref([]); //控制线条显示
-const leftList = ref([]);
-const rightList = ref([]);
-const domList = ref(new Map()); //获取 所有的ref
-const domListRight = ref(new Map()); //获取 所有的ref
-const listData = ref(props.checkData); //线条左右两侧的数据
-const emit = defineEmits(["result"]); //如果不加这个再次点击左侧会没有反应
-const setdom = (el, item) => {
-  //左侧dom
-  if (el) {
-    domList.value.set(item, el);
-  }
-};
-const setdomRight = (el, item) => {
-  //右侧dom
-  if (el) {
-    domListRight.value.set(item, el);
-  }
-};
-const processArray = (arr) => {
-  // ref_ied_id作为键,obj作为值
-  const uniqueObjects = new Map();
-  // 遍历数组
-  for (const obj of arr) {
-    const { ref_ied_id } = obj;
-    // 如果当前对象的 ref_ied_id 属性已经存在于 uniqueObjects 中
-    if (uniqueObjects.has(ref_ied_id)) {
-      // 将对应对象的 ref_type 属性设为 2,箭头双向
-      uniqueObjects.get(ref_ied_id).ref_type = 2;
-    } else {
-      // 否则,将当前对象添加到 uniqueObjects 中
-      uniqueObjects.set(ref_ied_id, obj);
-    }
-  }
-  // 将 uniqueObjects 中的值转为数组并返回
-  return Array.from(uniqueObjects.values());
-};
-//点击图片的时候筛选出数据
-const clickImg = (dataItem) => {
-  Object.values(props.iedRelation).find((item) => {
-    if (item.ied_name == dataItem.ref_ied_name) {
-      listData.value = item;
-    }
   });
-};
-watch(
-  () => props.checkData,
-  (newValue, oldV) => {
-    listData.value = newValue;
-    if (newValue && leaderLines.value.length > 0) {
-      // leaderLines.value.forEach((line) => line.remove()); //清除连线
-      leaderLines.value = [];
+  const middleElement = ref(null);
+  const myElement = ref(null);
+  let leaderLines = ref([]); //控制线条显示
+  const leftList = ref([]);
+  const rightList = ref([]);
+  const domList = ref(new Map()); //获取 所有的ref
+  const domListRight = ref(new Map()); //获取 所有的ref
+  const listData = ref(props.checkData); //线条左右两侧的数据
+  const emit = defineEmits(["result"]); //如果不加这个再次点击左侧会没有反应
+  const setdom = (el, item) => {
+    //左侧dom
+    if (el) {
+      domList.value.set(item, el);
     }
-  },
-  { deep: true }
-);
-watch(
-  () => listData.value,
-  (newValue) => {
-    emit("result", newValue);
-    clickResetLine();
-  }
-);
-watch(
-  () => props.isOpen,
-  (newValue) => {
-    if (newValue) {
-      domList.value.clear();
-      domListRight.value.clear();
-      leaderLines.value = [];
+  };
+  const setdomRight = (el, item) => {
+    //右侧dom
+    if (el) {
+      domListRight.value.set(item, el);
     }
-    nextTick(() => {
-      middleLinePosition();
-      removeLine();
+  };
+  const processArray = (arr) => {
+    // ref_ied_id作为键,obj作为值
+    const uniqueObjects = new Map();
+    // 遍历数组
+    for (const obj of arr) {
+      const { ref_ied_id } = obj;
+      // 如果当前对象的 ref_ied_id 属性已经存在于 uniqueObjects 中
+      if (uniqueObjects.has(ref_ied_id)) {
+        // 将对应对象的 ref_type 属性设为 2,箭头双向
+        uniqueObjects.get(ref_ied_id).ref_type = 2;
+      } else {
+        // 否则,将当前对象添加到 uniqueObjects 中
+        uniqueObjects.set(ref_ied_id, obj);
+      }
+    }
+    // 将 uniqueObjects 中的值转为数组并返回
+    return Array.from(uniqueObjects.values());
+  };
+  //点击图片的时候筛选出数据
+  const clickImg = (dataItem) => {
+    Object.values(props.iedRelation).find((item) => {
+      if (item.ied_name == dataItem.ref_ied_name) {
+        listData.value = item;
+      }
     });
-  }
-);
-//点击后重置数据和线条
-const clickResetLine = () => {
-  domList.value.clear();
-  domListRight.value.clear();
-  leaderLines.value = [];
-  middleLinePosition();
-  setLine();
-  removeLine();
-};
-// 将设备列表分成两份
-const bothSide = (data) => {
-  const formatArr = processArray(data);
-  const arrlenght = formatArr.length;
-  const long1 = Math.ceil(arrlenght / 2);
-  leftList.value = formatArr.splice(0, long1);
-  rightList.value = formatArr.splice(0);
-};
-
-const setLeaderline = () => {
-  // lineArr.value = [];
-  //线条样式
-  const lineStyle0 = {
-    color: "#51637F",
-    size: 2,
-    path: "straight",
-    startPlug: "arrow1",
-    endPlug: "behind",
-    startSocket: "right",
-    endSocket: "left",
   };
-  const lineStyle1 = {
-    color: "#51637F",
-    size: 2,
-    path: "straight",
-    endPlug: "arrow1",
-    startSocket: "right",
-    endSocket: "left",
+  watch(
+    () => props.checkData,
+    (newValue, oldV) => {
+      listData.value = newValue;
+      if (newValue && leaderLines.value.length > 0) {
+        // leaderLines.value.forEach((line) => line.remove()); //清除连线
+        leaderLines.value = [];
+      }
+    },
+    { deep: true }
+  );
+  watch(
+    () => listData.value,
+    (newValue) => {
+      emit("result", newValue);
+      clickResetLine();
+    }
+  );
+  watch(
+    () => props.isOpen,
+    (newValue) => {
+      if (newValue) {
+        domList.value.clear();
+        domListRight.value.clear();
+        leaderLines.value = [];
+      }
+      nextTick(() => {
+        middleLinePosition();
+        removeLine();
+      });
+    }
+  );
+  //点击后重置数据和线条
+  const clickResetLine = () => {
+    domList.value.clear();
+    domListRight.value.clear();
+    leaderLines.value = [];
+    middleLinePosition();
+    setLine();
+    removeLine();
   };
-  const lineStyle2 = {
-    color: "#134BEA",
-    size: 2,
-    path: "straight",
-    startPlug: "arrow1",
-    endPlug: "arrow1",
-    startSocket: "right",
-    endSocket: "left",
+  // 将设备列表分成两份
+  const bothSide = (data) => {
+    const formatArr = processArray(data);
+    const arrlenght = formatArr.length;
+    const long1 = Math.ceil(arrlenght / 2);
+    leftList.value = formatArr.splice(0, long1);
+    rightList.value = formatArr.splice(0);
   };
-  const lineStyleRight0 = {
-    color: "#51637F",
-    size: 2,
-    path: "straight",
-    startPlug: "arrow1",
-    endPlug: "behind",
-    startSocket: "left",
-    endSocket: "right",
+  
+  const setLeaderline = () => {
+    // lineArr.value = [];
+    //线条样式
+    const lineStyle0 = {
+      color: "#51637F",
+      size: 2,
+      path: "straight",
+      startPlug: "arrow1",
+      endPlug: "behind",
+      startSocket: "right",
+      endSocket: "left",
+    };
+    const lineStyle1 = {
+      color: "#51637F",
+      size: 2,
+      path: "straight",
+      endPlug: "arrow1",
+      startSocket: "right",
+      endSocket: "left",
+    };
+    const lineStyle2 = {
+      color: "#134BEA",
+      size: 2,
+      path: "straight",
+      startPlug: "arrow1",
+      endPlug: "arrow1",
+      startSocket: "right",
+      endSocket: "left",
+    };
+    const lineStyleRight0 = {
+      color: "#51637F",
+      size: 2,
+      path: "straight",
+      startPlug: "arrow1",
+      endPlug: "behind",
+      startSocket: "left",
+      endSocket: "right",
+    };
+    const lineStyleRight1 = {
+      color: "#51637F",
+      size: 2,
+      path: "straight",
+      endPlug: "arrow1",
+      startSocket: "left",
+      endSocket: "right",
+    };
+    const lineStyleRight2 = {
+      color: "#134BEA",
+      size: 2,
+      path: "straight",
+      startPlug: "arrow1",
+      endPlug: "arrow1",
+      startSocket: "left",
+      endSocket: "right",
+    };
+    const startDom = document.getElementById("end");
+    //循环画线
+    for (const [key, value] of domList.value) {
+      const endDom = value;
+      let line;
+      if (key.ref_type == 0) {
+        line = new LeaderLine(endDom, startDom, lineStyle0);
+      } else if (key.ref_type == 1) {
+        line = new LeaderLine(endDom, startDom, lineStyle1);
+      } else if (key.ref_type == 2) {
+        line = new LeaderLine(endDom, startDom, lineStyle2);
+      }
+      //  保存进数组,方便进行遍历删除
+      leaderLines.value.push(line);
+    }
+    //循环画线右侧
+    for (const [key, value] of domListRight.value) {
+      const endDom = value;
+      let line2;
+      if (key.ref_type == 0) {
+        line2 = new LeaderLine(endDom, startDom, lineStyleRight0);
+      } else if (key.ref_type == 1) {
+        line2 = new LeaderLine(endDom, startDom, lineStyleRight1);
+      } else if (key.ref_type == 2) {
+        line2 = new LeaderLine(endDom, startDom, lineStyleRight2);
+      }
+      //  保存进数组,方便进行遍历删除
+      leaderLines.value.push(line2);
+    }
+    hiddenLine();
+  };
+  //设置中间盒子的所在位置
+  const middleLinePosition = () => {
+    setTimeout(() => {
+      const heights = myElement.value.scrollHeight;
+      if (leftList.value.length > 3 || rightList.value.length > 3) {
+        middleElement.value.style.marginTop = `${(heights - 60) / 2}px`; // 设置元素的垂直位置
+      } else {
+        middleElement.value.style.marginTop = "150px"; // 设置元素的垂直位置
+      }
+    }, 0);
   };
-  const lineStyleRight1 = {
-    color: "#51637F",
-    size: 2,
-    path: "straight",
-    endPlug: "arrow1",
-    startSocket: "left",
-    endSocket: "right",
+  onMounted(() => {
+    nextTick(() => {
+      setLine();
+      middleLinePosition();
+      nextTick(() => {
+        newPositionLine();
+      });
+    });
+  });
+  //滚动时重定位线条
+  const newPositionLine = () => {
+    document.getElementById("treedom").addEventListener(
+      "scroll",
+      AnimEvent.add(() => {
+        leaderLines.value.forEach((line) => {
+          hiddenLine();
+          line.position();
+          line.positionByWindowResize = false;
+        });
+        //中间展示图片的
+      }),
+      false
+    );
   };
-  const lineStyleRight2 = {
-    color: "#134BEA",
-    size: 2,
-    path: "straight",
-    startPlug: "arrow1",
-    endPlug: "arrow1",
-    startSocket: "left",
-    endSocket: "right",
+  //弹窗打开后使得线条在指定区域中
+  const hiddenLine = () => {
+    const elmWrapper = document.getElementById("wrapper");
+    // 移动 line
+    document.body.querySelectorAll("body .leader-line").forEach((node) => {
+      elmWrapper.appendChild(node);
+    });
+    elmWrapper.style.transform = "none";
+    var rectWrapper = elmWrapper.getBoundingClientRect();
+    // Move to the origin of coordinates as the document
+    elmWrapper.style.transform = `translate(${
+      (rectWrapper.left + window.scrollY) * -1
+    }px, ${(rectWrapper.top + window.scrollX) * -1}px)`;
   };
-  const startDom = document.getElementById("end");
-  //循环画线
-  for (const [key, value] of domList.value) {
-    const endDom = value;
-    let line;
-    if (key.ref_type == 0) {
-      line = new LeaderLine(endDom, startDom, lineStyle0);
-    } else if (key.ref_type == 1) {
-      line = new LeaderLine(endDom, startDom, lineStyle1);
-    } else if (key.ref_type == 2) {
-      line = new LeaderLine(endDom, startDom, lineStyle2);
+  const setLine = () => {
+    if (listData.value) {
+      bothSide(listData.value.list);
     }
-    //  保存进数组,方便进行遍历删除
-    leaderLines.value.push(line);
+    setTimeout(() => {
+      setLeaderline();
+    }, 30);
+  };
+  const removeLine = () => {
+    const elmWrapper = document.getElementById("wrapper");
+    document.body.querySelectorAll("body .leader-line").forEach((node) => {
+      elmWrapper.removeChild(node);
+    });
+  };
+  </script>
+  <style lang="scss">
+  @mixin img-size {
+    width: 150px;
+    height: 90px;
+    margin-bottom: 10px;
   }
-  //循环画线右侧
-  for (const [key, value] of domListRight.value) {
-    const endDom = value;
-    let line2;
-    if (key.ref_type == 0) {
-      line2 = new LeaderLine(endDom, startDom, lineStyleRight0);
-    } else if (key.ref_type == 1) {
-      line2 = new LeaderLine(endDom, startDom, lineStyleRight1);
-    } else if (key.ref_type == 2) {
-      line2 = new LeaderLine(endDom, startDom, lineStyleRight2);
-    }
-    //  保存进数组,方便进行遍历删除
-    leaderLines.value.push(line2);
+  @mixin left-and-right {
+    display: flex;
+    flex-direction: column;
+  }
+  .main-cont {
+    display: flex;
+    justify-content: space-evenly;
+  }
+  
+  .leader-line {
+    z-index: 3000;
   }
-  hiddenLine();
-};
-//设置中间盒子的所在位置
-const middleLinePosition = () => {
-  setTimeout(() => {
-    const heights = myElement.value.scrollHeight;
-    if (leftList.value.length > 3 || rightList.value.length > 3) {
-      middleElement.value.style.marginTop = `${(heights - 60) / 2}px`; // 设置元素的垂直位置
-    } else {
-      middleElement.value.style.marginTop = "150px"; // 设置元素的垂直位置
+  .main-left {
+    display: flex;
+    @include left-and-right;
+    .img-item {
+      @include img-size;
     }
-  }, 0);
-};
-onMounted(() => {
-  nextTick(() => {
-    setLine();
-    middleLinePosition();
-    nextTick(() => {
-      newPositionLine();
-    });
-  });
-});
-//滚动时重定位线条
-const newPositionLine = () => {
-  document.getElementById("treedom").addEventListener(
-    "scroll",
-    AnimEvent.add(() => {
-      leaderLines.value.forEach((line) => {
-        hiddenLine();
-        line.position();
-        line.positionByWindowResize = false;
-      });
-      //中间展示图片的
-    }),
-    false
-  );
-};
-//弹窗打开后使得线条在指定区域中
-const hiddenLine = () => {
-  const elmWrapper = document.getElementById("wrapper");
-  // 移动 line
-  document.body.querySelectorAll("body .leader-line").forEach((node) => {
-    elmWrapper.appendChild(node);
-  });
-  elmWrapper.style.transform = "none";
-  var rectWrapper = elmWrapper.getBoundingClientRect();
-  // Move to the origin of coordinates as the document
-  elmWrapper.style.transform = `translate(${
-    (rectWrapper.left + window.scrollY) * -1
-  }px, ${(rectWrapper.top + window.scrollX) * -1}px)`;
-};
-const setLine = () => {
-  if (listData.value) {
-    bothSide(listData.value.list);
   }
-  setTimeout(() => {
-    setLeaderline();
-  }, 30);
-};
-const removeLine = () => {
-  const elmWrapper = document.getElementById("wrapper");
-  document.body.querySelectorAll("body .leader-line").forEach((node) => {
-    elmWrapper.removeChild(node);
-  });
-};
-</script>
-<style lang="scss">
-@mixin img-size {
-  width: 150px;
-  height: 90px;
-  margin-bottom: 10px;
-}
-@mixin left-and-right {
-  display: flex;
-  flex-direction: column;
-}
-.main-cont {
-  display: flex;
-  justify-content: space-evenly;
-}
-
-.leader-line {
-  z-index: 3000;
-}
-.main-left {
-  display: flex;
-  @include left-and-right;
-  .img-item {
-    @include img-size;
+  .main-middle {
+    box-sizing: border-box;
+    img {
+      margin-bottom: 10px;
+    }
+    .middle-item {
+      @include left-and-right;
+      align-items: center;
+      color: #ffcb11;
+    }
   }
-}
-.main-middle {
-  box-sizing: border-box;
-  img {
-    margin-bottom: 10px;
+  .main-right {
+    display: flex;
+    @include left-and-right;
+    .img-item {
+      @include img-size;
+    }
   }
-  .middle-item {
+  
+  .cont {
     @include left-and-right;
     align-items: center;
-    color: #ffcb11;
-  }
-}
-.main-right {
-  display: flex;
-  @include left-and-right;
-  .img-item {
-    @include img-size;
+    margin-bottom: 10px;
+    .ied-desc {
+      color: #255ce7;
+    }
   }
-}
-
-.cont {
-  @include left-and-right;
-  align-items: center;
-  margin-bottom: 10px;
-  .ied-desc {
-    color: #255ce7;
+  
+  #wrapper {
+    width: 0;
+    height: 0;
+    position: relative; /* Origin of coordinates for lines, and scrolled content (i.e. not `absolute`) */
   }
-}
-
-#wrapper {
-  width: 0;
-  height: 0;
-  position: relative; /* Origin of coordinates for lines, and scrolled content (i.e. not `absolute`) */
-}
-</style>
+  </style>

+ 6 - 6
src/pages/netStructPicture/components/scdVisual.vue

@@ -161,7 +161,7 @@ import scdDialogIndex from "./scdDialogIndex";
 const userStoreCode = useDataStore();
 const data = reactive({
   queryParams: {
-    scd_id: 296000081,
+    scd_id: 452000123,
   },
 });
 const loading = ref(true);
@@ -171,7 +171,7 @@ const { queryParams } = toRefs(data);
 // 表单重置
 const reset = () => {
   queryParams.value = {
-    scd_id: 296000081,
+    scd_id: 452000123,
     voltage_level_id: null, //电压等级
     area_id: null, //间隔
     device_type_id: null, //装置类型
@@ -185,7 +185,7 @@ const allIedType = [{ name: "全部", code: "alls" }];
 const voltageLevel = ref([{ name: "全部", id: "alls" }]); //电压等级
 const areaType = ref([]);
 const getArea = async () => {
-  const areaRes = await areaList({ scd_id: 296000081 });
+  const areaRes = await areaList({ scd_id: 452000123 });
   if (!areaRes.data) {
     voltageLevel.value = [];
     loading.value = false;
@@ -223,7 +223,7 @@ const getArea = async () => {
 //设备类型
 const iedTypeData = ref([]);
 const getTypelist = async () => {
-  const typeRes = await iedTypelist({ scd_id: 296000081 });
+  const typeRes = await iedTypelist({ scd_id: 452000123 });
   iedTypeData.value = typeRes.data ? [...allIedType, ...typeRes.data] : [];
 };
 const iedName = ref([]);
@@ -254,7 +254,7 @@ const searchInput = (value) => {
 const count = ref(0);
 const iedNameData = async () => {
   //IED编码或名称
-  const iedRes = await scdIedRelation({ scd_id: 296000081 });
+  const iedRes = await scdIedRelation({ scd_id: 452000123 });
   iedName.value = iedRes.data;
   count.value = iedRes.count;
 };
@@ -282,7 +282,7 @@ const changeLevel = async (value, mainValue) => {
     open.value = true;
     dialogData.value = iedName.value;
   } else if (value) {
-    const mainClick = { scd_id: 296000081, area_id: value };
+    const mainClick = { scd_id: 452000123, area_id: value };
     const forms = mainValue ? mainClick : queryParams.value;
     open.value = true;
     dialogData.value = [];

+ 512 - 5
src/pages/netStructPicture/components/virtualRelation.vue

@@ -1,15 +1,522 @@
-<!-- 虚端子关系 -->
+<!-- 输入输出控制块 -->
 <template>
-  <div>3</div>
+  <div>
+    <!-- 关联图 -->
+    <div class="main-cont" id="treedom3">
+      <div class="main-left">
+        <div
+          v-for="(item, index) in leftList"
+          :key="index"
+          class="conts"
+          @click="clickImg(item)"
+        >
+          <div class="cont-title">
+            <img :src="devicePng" alt="" class="img-item" />
+            <div class="cont-item">
+              <div>{{ item.ref_ied_desc }}</div>
+              <div>{{ item.ref_ied_name }}</div>
+            </div>
+          </div>
+          <div
+            v-for="(cItem, index2) in item.titleItems"
+            :key="index2"
+            :ref="(el) => setdomLeftChild3(el, cItem)"
+          >
+            <div class="text-midle">
+              <div>
+                {{
+                  `${cItem.attr_ld_inst}/${cItem.attr_prefix}${cItem.attr_ln_class}.${cItem.attr_do_name}.${cItem.attr_da_name}`
+                }}
+              </div>
+              <div>{{ cItem.do_source_desc }}</div>
+            </div>
+          </div>
+        </div>
+      </div>
+      <!-- 中间部分 -->
+      <div class="main-middle" ref="middleHeight" id="end">
+        <div class="cont-title">
+          <img :src="devicePng" alt="" class="img-item" />
+          <div class="middle-title">
+            <div>{{ listData3.desc }}</div>
+            <div>{{ listData3.ied_name }}</div>
+          </div>
+        </div>
+        <div class="middle-item">
+          <div
+            class="midle-cont"
+            v-for="(item, index) in svInfo"
+            :key="index"
+            :ref="(el) => setdomMiddle3(el, item)"
+          >
+            <div style="margin: 0 4px" v-if="!item.isDecollate">
+              {{ item.no }}
+            </div>
+            <div class="midlestyle" v-if="!item.isDecollate">
+              <div v-if="item.attr_da_name == 'general'">
+                {{
+                  `${item.attr_ld_inst}/${item.attr_ln_class}${item.attr_ln_inst}.${item.attr_do_name}.${item.attr_da_name}`
+                }}
+              </div>
+              <div v-else>{{ item.attr_int_addr }}</div>
+              <div>{{ item.do_source_desc }}</div>
+            </div>
+            <div class="midlestyle omit" v-if="item.isDecollate">
+              {{ item.do_source_desc }}
+            </div>
+          </div>
+        </div>
+      </div>
+      <!-- 右侧 -->
+      <div class="main-right">
+        <div
+          v-for="(item, index) in rightList"
+          :key="index"
+          class="conts"
+          @click="clickImg(item)"
+        >
+          <div class="cont-title">
+            <img :src="devicePng" alt="" class="img-item" />
+            <div class="cont-item">
+              <div>{{ item.ref_ied_desc }}</div>
+              <div>{{ item.ref_ied_name }}</div>
+            </div>
+          </div>
+          <div
+            v-for="(cItem, index2) in item.titleItems"
+            :key="index2"
+            :ref="(el) => setdomRightChild3(el, cItem)"
+          >
+            <div class="text-midle">
+              <div>{{ `${cItem.attr_int_addr}` }}</div>
+              <div>{{ cItem.do_target_desc }}</div>
+            </div>
+          </div>
+        </div>
+      </div>
+      <div id="wrapper"></div>
+    </div>
+  </div>
 </template>
 <script setup>
-import { onMounted, watch, ref,nextTick,defineEmits } from "vue";
+import { onMounted, watch, ref, nextTick, defineEmits, inject } from "vue";
+import devicePng from "@/assets/image/instruct/device.png";
+import LeaderLine from "../../../../public/leader-line.min.js";
+import AnimEvent from "../../../../public/anim-event.min.js";
+import {
+  //send发送
+  iednetworkInfo,
+  iedSVSendCtrlblock,
+  gooseSendctrlblock,
+  //虚短子关系
+  getMiddleinputs,
+} from "@/api/iedNetwork";
+import jsPlumb from "jsplumb";
 const props = defineProps({
-  checkDialogData: {
+  checkData: {
     type: Object,
     default: () => {},
   },
+  checkData: {
+    type: Object,
+    default: () => {},
+  },
+  isOpen: {
+    type: Boolean,
+    default: false,
+  },
+  iedRelation: {
+    type: Object,
+    default: () => {},
+  },
+  tabName: {
+    type: String,
+    default: "",
+  },
+});
+const svInfo = ref(null);
+const scdIdValue = inject("scdId");
+
+//处理两边的数据
+const processBoth = (list, svResInfo, inoutType) => {
+  list.forEach((item, index) => {
+    item.titleItems = [];
+    svResInfo.data.forEach((key) => {
+      if (key.ied_name == item.ref_ied_name) {
+        if (
+          (item.titleItems.length < 1 && key.inout_type == inoutType) ||
+          (!item.titleItems.some(
+            (titleKey) => titleKey.ref_ied_name == key.ied_name
+          ) &&
+            key.inout_type === inoutType)
+        ) {
+          item.titleItems.push(key);
+        }
+      }
+    });
+  });
+};
+let leaderLines3 = ref([]); //控制线条显示
+const leftList = ref([]);
+const rightList = ref([]);
+const domListMiddle3 = ref(new Map()); //获取中间所有的ref
+const domListRightChild3 = ref(new Map()); //获取右侧所有子的ref
+const domListLeftChild3 = ref(new Map()); //获取中间所有子的ref
+const listData3 = ref(props.checkData); //线条左右两侧的数据
+const emit = defineEmits(["result"]); //如果不加这个再次点击左侧会没有反应
+const setdomMiddle3 = (el, item) => {
+  // 中间dom
+  if (el) {
+    domListMiddle3.value.set(item, el);
+  }
+};
+//左侧子Dom
+const setdomLeftChild3 = (el, item) => {
+  if (el) {
+    domListLeftChild3.value.set(item, el);
+  }
+};
+//右侧子Dom
+const setdomRightChild3 = (el, item) => {
+  if (el) {
+    domListRightChild3.value.set(item, el);
+  }
+};
+//得到中间的子版块数据
+const getNetworkInfo3 = async () => {
+  const svResInfo = await getMiddleinputs({
+    scd_id: scdIdValue,
+    ied_name: props.checkData.ied_name,
+  });
+  const data = {
+    attr_ld_inst: "",
+    attr_ln_class: "",
+    attr_ln_inst: "",
+    attr_do_name: "",
+    attr_da_name: "",
+    do_source_desc: "...",
+    no: "",
+    isDecollate: true,
+  };
+  //处理两边的数据
+  if (svResInfo.data.length > 0) {
+    processBoth(leftList.value, svResInfo, "in");
+    processBoth(rightList.value, svResInfo, "out");
+  }
+
+  //处理中间的数据有省略号的
+  let newData = [];
+  for (let i = 0; i < svResInfo.data.length; i++) {
+    newData.push(svResInfo.data[i]);
+    if (
+      i < svResInfo.data.length - 1 &&
+      svResInfo.data[i].ied_name != svResInfo.data[i + 1].ied_name
+    ) {
+      newData.push(data);
+    }
+  }
+
+  svInfo.value = newData;
+};
+const processArray = (arr) => {
+  // ref_ied_id作为键,obj作为值
+  const uniqueObjects = new Map();
+  // 遍历数组
+  for (const obj of arr) {
+    const { ref_ied_id } = obj;
+    // 如果当前对象的 ref_ied_id 属性已经存在于 uniqueObjects 中
+    if (uniqueObjects.has(ref_ied_id)) {
+      // 将对应对象的 ref_type 属性设为 2,箭头双向
+      uniqueObjects.get(ref_ied_id).ref_type = 2;
+    } else {
+      // 否则,将当前对象添加到 uniqueObjects 中
+      uniqueObjects.set(ref_ied_id, obj);
+    }
+  }
+  // 将 uniqueObjects 中的值转为数组并返回
+  return Array.from(uniqueObjects.values());
+};
+//点击图片的时候筛选出数据
+const clickImg = (dataItem) => {
+  Object.values(props.iedRelation).find((item) => {
+    if (item.ied_name == dataItem.ref_ied_name) {
+      listData3.value = item;
+    }
+  });
+};
+
+//点击后重置数据和线条
+const clickResetLine3 = () => {
+  domListMiddle3.value.clear();
+  domListLeftChild3.value.clear();
+  domListRightChild3.value.clear();
+  leaderLines3.value = [];
+  setLine();
+  removeLine3();
+};
+
+// 将设备列表分成两份
+const bothSide = (data) => {
+  leftList.value = [];
+  rightList.value = [];
+  data.forEach((item) => {
+    if (item.ref_type == 2 || item.ref_type == 1) {
+      item.titleItems = [];
+      leftList.value.push(item);
+    } else {
+      item.titleItems = [];
+      rightList.value.push(item);
+    }
+  });
+};
+const setLeaderline = () => {
+  //左侧子组件
+  for (let [key, value] of domListMiddle3.value) {
+    for (const [key2, value2] of domListLeftChild3.value) {
+      const endDom = value2;
+      if (key.node_id == key2.node_id) {
+        const line = new LeaderLine(endDom, value, {
+          color: "#7484AB",
+          size: 2,
+          path: "straight",
+          startSocket: "right",
+          endSocket: "left",
+          y: 50,
+          startPlug: "disc",
+          endPlug: "arrow1",
+        });
+        leaderLines3.value.push(line);
+      }
+    }
+  }
+  for (let [key, value] of domListMiddle3.value) {
+    //右侧子组件
+    for (const [key2, value2] of domListRightChild3.value) {
+      const endDom = value2;
+      if (key.node_id == key2.node_id) {
+        const line2 = new LeaderLine(value, endDom, {
+          color: "#7484AB",
+          size: 2,
+          path: "straight",
+          startSocket: "right",
+          endSocket: "left",
+          startPlug: "disc",
+          endPlug: "arrow1",
+        });
+        leaderLines3.value.push(line2);
+      }
+    }
+  }
+  hiddenLine();
+};
+//滚动时重定位线条
+const newPositionLine = () => {
+  document.getElementById("treedom3").addEventListener(
+    "scroll",
+    AnimEvent.add(() => {
+      leaderLines3.value.forEach((line) => {
+        if (line) {
+          hiddenLine();
+          line.position();
+          line.positionByWindowResize = false;
+        }
+      });
+      //中间展示图片的
+    }),
+    false
+  );
+};
+//弹窗打开后使得线条在指定区域中
+const hiddenLine = () => {
+  const elmWrapper = document.getElementById("wrapper");
+  // 移动 line
+  document.body.querySelectorAll("body .leader-line").forEach((node) => {
+    elmWrapper.appendChild(node);
+  });
+  elmWrapper.style.transform = "none";
+  var rectWrapper = elmWrapper.getBoundingClientRect();
+  // Move to the origin of coordinates as the document
+  elmWrapper.style.transform = `translate(${
+    (rectWrapper.left + window.scrollY) * -1
+  }px, ${(rectWrapper.top + window.scrollX) * -1}px)`;
+};
+const setLine = () => {
+  if (listData3.value) {
+    bothSide(listData3.value.list);
+  }
+  setTimeout(() => {
+    setLeaderline();
+  }, 500);
+};
+
+const removeLine3 = () => {
+  leaderLines3.value = [];
+  const elmWrapper = document.getElementById("wrapper");
+  document.body.querySelectorAll("body .leader-line").forEach((node) => {
+    elmWrapper.removeChild(node);
+  });
+};
+onMounted(() => {
+  if (props.checkData != null) {
+    getNetworkInfo3();
+  }
+  //不加条件切换下方tab时会出现bug
+  nextTick(() => {
+    setLine();
+    nextTick(() => {
+      newPositionLine();
+    });
+  });
 });
+watch(
+  () => props.checkData,
+  (newValue) => {
+    listData3.value = [];
+    svInfo.value = [];
+    listData3.value = newValue;
+    if (newValue != null) {
+      getNetworkInfo3();
+    }
+    clickResetLine3();
+    emit("result", newValue);
+    if (newValue && leaderLines3.value.length > 0) {
+      leaderLines3.value = [];
+    }
+  }
+);
+watch(
+  () => props.isOpen,
+  (newValue) => {
+    if (newValue) {
+      domListMiddle3.value.clear();
+      domListLeftChild3.value.clear();
+      domListRightChild3.value.clear();
+      leaderLines3.value = [];
+    }
+    nextTick(() => {
+      removeLine3();
+    });
+  }
+);
 </script>
-<style scoped lang="scss">
+<style lang="scss"  scoped>
+@mixin img-size {
+  width: 48px;
+  height: 48px;
+}
+@mixin left-and-right {
+  display: flex;
+  flex-direction: column;
+}
+.main-cont {
+  display: flex;
+  justify-content: space-evenly;
+}
+
+.leader-line {
+  z-index: 3000;
+}
+.main-left {
+  display: flex;
+  @include left-and-right;
+}
+.main-middle {
+  box-sizing: border-box;
+  border: 2px dashed #98a8ff;
+  background: #edf3ff;
+  margin:0 60px;
+  img {
+    @include img-size;
+  }
+  .middle-item {
+    @include left-and-right;
+    align-items: center;
+    cursor: pointer;
+  }
+  .cont-title {
+    display: flex;
+    align-items: center;
+    margin: 12px 14px 5px 14px;
+    border-bottom: 1px solid #a3ade0;
+  }
+  .middle-title {
+    color: #ffcb11;
+    margin-left: 8px;
+  }
+}
+.main-right {
+  display: flex;
+  @include left-and-right;
+  .img-item {
+    @include img-size;
+  }
+}
+
+.conts {
+  @include left-and-right;
+  margin-bottom: 24px;
+  border: 2px dashed #98a8ff;
+  cursor: pointer;
+  background: #f7f8fb;
+  padding: 12px;
+  .cont-title {
+    display: flex;
+    align-items: center;
+    margin: 12px 14px 5px 14px;
+    padding: 12px;
+    border-bottom: 1px solid #a3ade0;
+  }
+  .cont-item {
+    color: #1a2447;
+    margin-left: 6px;
+    vertical-align: middle;
+  }
+  .ied-desc {
+    color: #255ce7;
+  }
+  .ied-desc-title {
+    color: #134bea;
+  }
+
+  .ied-desc-child-title {
+    color: #5182ff;
+    margin-left: 14px;
+    display: block;
+  }
+}
+.midle-cont {
+  display: flex;
+  border: 1px solid #7484ab;
+  align-items: center;
+  width: 94%;
+  margin-bottom: 8px;
+  color: #1a2447;
+}
+.ied-desc-child,
+.midlestyle {
+  @include left-and-right;
+  align-items: center;
+  border-radius: 2px;
+  padding: 5px;
+  color: #1a2447;
+  flex: 1;
+}
+.omit {
+  font-weight: bold;
+  letter-spacing: 8px;
+  font-size: 15px;
+}
+#wrapper {
+  width: 0;
+  height: 0;
+  position: relative; /* Origin of coordinates for lines, and scrolled content (i.e. not `absolute`) */
+}
+.text-midle {
+  text-align: center;
+  width: 94%;
+  border: 1px solid #7484ab;
+  margin-bottom: 8px;
+  border-radius: 2px;
+  padding: 5px;
+}
 </style>

+ 1 - 0
src/router/index.js

@@ -75,6 +75,7 @@ const routes = [
                 }
             },
             {
+                
                 path:"/home/netStructPicture",
                 component:()=>import("@/pages/netStructPicture"),
                 meta:{