Explorar o código

输入输出控制块

“yueshang” hai 1 ano
pai
achega
18268e7a96

+ 505 - 5
src/pages/netStructPicture/components/inoutControl.vue

@@ -1,7 +1,88 @@
 <!-- 输入输出控制块 -->
 <template>
   <div>
-    <div v-if="isPhoto == 'photo'">关联图</div>
+    <!-- 关联图 -->
+    <div
+      v-if="isPhoto == 'photo'"
+      class="main-cont"
+      ref="myElement"
+      id="treedom"
+    >
+      <div class="main-left">
+        <div
+          v-for="(item, index) in leftList"
+          :key="index"
+          class="conts"
+          @click="clickImg(item)"
+          :ref="(el) => setdom(el, item)"
+        >
+          <div class="cont-item">
+            <div>{{ item.ref_ied_name }}</div>
+            <div>{{ item.ref_ied_desc }}</div>
+          </div>
+          <div v-for="(cItem, index2) in item.titleItems" :key="index2">
+            <div class="ied-desc-child-title">
+              {{ cItem.ld_inst }} {{ cItem.ld_desc }}
+            </div>
+            <div
+              class="ied-desc-child"
+              v-for="(item3, index3) in cItem.childItem"
+              :key="index3"
+            >
+              <div>{{ item3.ctrl_name }}</div>
+              <div>{{ item3.datset_desc }}</div>
+            </div>
+          </div>
+        </div>
+      </div>
+      <!-- 中间部分 -->
+      <div class="main-middle" ref="middleHeight">
+        <div class="middle-title">
+          <div v-if="listData">{{ listData.ied_name }}</div>
+          <div v-if="listData">{{ listData.desc }}</div>
+        </div>
+        <div class="middle-item" ref="middleElement">
+          <div
+            class="ied-desc-child middle-child"
+            v-for="(itemChild, index) in svInfo"
+            :key="index"
+          >
+            <div>{{ `${itemChild.ldinst}/${itemChild.attr_name}` }}</div>
+            <div>{{ itemChild.datset_desc }}</div>
+            <div class="ied-desc">APPID:{{ itemChild.APPID }}</div>
+          </div>
+        </div>
+      </div>
+      <!-- 右侧 -->
+      <div class="main-right">
+        <div
+          v-for="(item, index) in rightList"
+          :key="index"
+          class="conts"
+          @click="clickImg(item)"
+          :ref="(el) => setdomRight(el, item)"
+        >
+          <div class="cont-item">
+            <div>{{ item.ref_ied_name }}</div>
+            <div>{{ item.ref_ied_desc }}</div>
+          </div>
+          <div v-for="(cItem, index2) in item.titleItems" :key="index2">
+            <div class="ied-desc-child-title">
+              {{ cItem.ld_inst }} {{ cItem.ld_desc }}
+            </div>
+            <div
+              class="ied-desc-child"
+              v-for="(item3, index3) in cItem.childItem"
+              :key="index3"
+            >
+              <div>{{ item3.ctrl_name }}</div>
+              <div>{{ item3.datset_desc }}</div>
+            </div>
+          </div>
+        </div>
+      </div>
+      <div id="wrapper"></div>
+    </div>
     <div v-if="isPhoto != 'photo'">
       <inoutSendOrRv
         :isPhoto="isPhoto"
@@ -13,11 +94,17 @@
 </template>
 <script setup>
 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 inoutSendOrRv from "./inoutSendOrRv";
 import {
   //send发送
   iednetworkInfo,
+  iedSVSendCtrlblock,
+  gooseSendctrlblock,
 } from "@/api/iedNetwork";
+import jsPlumb from "jsplumb";
 const props = defineProps({
   checkData: {
     type: Object,
@@ -27,27 +114,359 @@ const props = defineProps({
     type: String,
     default: "",
   },
+  checkData: {
+    type: Object,
+    default: () => {},
+  },
+  isOpen: {
+    type: Boolean,
+    default: false,
+  },
+  iedRelation: {
+    type: Object,
+    default: () => {},
+  },
 });
 const svInfo = ref(null);
 const scdIdValue = inject("scdId");
+const iedChild = ref(null);
+//得到两边子版块的数据
+const getIedChild = async () => {
+  const childRes = await gooseSendctrlblock({
+    scd_id: scdIdValue,
+    ied_name: props.checkData.ied_name,
+    forcerefresh: 0,
+  });
+  //左右侧内部侧数据组装
+  listData.value.list.forEach((item, index) => {
+    item.titleItems = [];
+    childRes.data.forEach((key) => {
+      if (key.target_ied_name === item.ref_ied_name) {
+        if (
+          item.titleItems.length < 1 ||
+          !item.titleItems.some(
+            (titleKey) =>
+              titleKey.ld_inst === key.ld_inst &&
+              titleKey.ld_desc === key.ld_desc
+          )
+        ) {
+          key.childItem = [];
+          item.titleItems.push(key);
+        }
+        item.titleItems.forEach((itemKey) => {
+          if (
+            itemKey.ld_inst == key.ld_inst &&
+            itemKey.ld_desc == key.ld_desc
+          ) {
+            itemKey.childItem.push(key);
+          }
+        });
+      }
+    });
+  });
+  iedChild.value = childRes.data;
+};
+
+//得到中间的子版块数据
 const getNetworkInfo = async () => {
   const svResInfo = await iednetworkInfo({
     scd_id: scdIdValue,
     ied_name: props.checkData.ied_name,
   });
-  svInfo.value = svResInfo.data;
+  const svResInfoCtrl = await iedSVSendCtrlblock({
+    scd_id: scdIdValue,
+    ied_name: props.checkData.ied_name,
+  });
+  if (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
+            : "";
+      });
+    });
+    svInfo.value = svResInfoCtrl.data.GSEControl;
+  }
 };
 watch(
   () => props.checkData,
   (newValue) => {
-    getNetworkInfo();
+    if (newValue != null) {
+      getIedChild();
+      getNetworkInfo();
+    }
+  }
+);
+//线条
+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 = [];
+    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 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",
+  };
+  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 == 2) {
+      // line = new LeaderLine(endDom, startDom, lineStyle0);
+    }
+    //  保存进数组,方便进行遍历删除
+    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 == 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
+    } else {
+      middleElement.value.style.marginTop = "150px"; // 设置元素的垂直位置
+    }
+  }, 0);
+};
+//滚动时重定位线条
+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);
+  });
+};
 onMounted(() => {
-  getNetworkInfo();
+  if (props.checkData != null) {
+    getIedChild();
+    getNetworkInfo();
+  }
+  //不加条件切换下方tab时会出现bug
+  if (props.isPhoto == "photo") {
+    nextTick(() => {
+      setLine();
+      middleLinePosition();
+      nextTick(() => {
+        newPositionLine();
+      });
+    });
+  }
 });
+watch(
+  () => props.isPhoto,
+  (newValue) => {
+    if (props.isPhoto == "photo") {
+      nextTick(() => {
+        setLine();
+        middleLinePosition();
+        nextTick(() => {
+          newPositionLine();
+        });
+      });
+    }
+  }
+);
 </script>
-<style lang="scss">
+<style lang="scss"  scoped>
 @mixin img-size {
   width: 150px;
   height: 90px;
@@ -57,4 +476,85 @@ onMounted(() => {
   display: flex;
   flex-direction: column;
 }
+.main-cont {
+  display: flex;
+  justify-content: space-evenly;
+  overflow-y: auto;
+  height: 65vh;
+}
+
+.leader-line {
+  z-index: 3000;
+}
+.main-left {
+  display: flex;
+  @include left-and-right;
+}
+.main-middle {
+  box-sizing: border-box;
+  border: 2px dashed #98a8ff;
+  img {
+    margin-bottom: 10px;
+  }
+  .middle-item {
+    @include left-and-right;
+    align-items: center;
+    cursor: pointer;
+  }
+  .middle-title {
+    margin: 12px;
+    text-align: center;
+    color: #ffcb11;
+    border-bottom: 1px solid #a3ade0;
+  }
+  .middel-child {
+  }
+}
+.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;
+  .cont-item {
+    @include left-and-right;
+    color: #1a2447;
+    align-items: center;
+    margin: 12px 14px 5px 14px;
+    border-bottom: 1px solid #a3ade0;
+  }
+  .ied-desc {
+    color: #255ce7;
+  }
+  .ied-desc-title {
+    color: #134bea;
+  }
+
+  .ied-desc-child-title {
+    color: #5182ff;
+    margin-left: 14px;
+    display: block;
+  }
+}
+.ied-desc-child {
+  @include left-and-right;
+  align-items: center;
+  border: 1px solid #7484ab;
+  border-radius: 2px;
+  margin: 12px 14px;
+  padding: 5px;
+  color: #1a2447;
+}
+#wrapper {
+  width: 0;
+  height: 0;
+  position: relative; /* Origin of coordinates for lines, and scrolled content (i.e. not `absolute`) */
+}
 </style>

+ 8 - 5
src/pages/netStructPicture/components/inoutSendOrRv.vue

@@ -331,7 +331,7 @@ const getGooseReList = async () => {
   });
   //子列表的数据
   gooseReCtrl.value.forEach((item) => {
-    if (gooseReCtrl.value.length > 0&& gooseRes.data.length>0) {
+    if (gooseReCtrl.value.length > 0 && gooseRes.data.length > 0) {
       gooseRes.data.forEach((key) => {
         if (
           item.out_dataset_name == key.out_dataset_name &&
@@ -344,7 +344,8 @@ const getGooseReList = async () => {
       });
     }
   });
-  gooseReList.value = gooseReCtrl.value.length>0 ? gooseReCtrl.value[0].children : null;
+  gooseReList.value =
+    gooseReCtrl.value.length > 0 ? gooseReCtrl.value[0].children : null;
 };
 const svSendRowClick4 = (row, column) => {
   ctrlId.value = row.node_id;
@@ -362,9 +363,11 @@ watch(
     svTableFcd.value = [];
     gooseList.value = [];
     gooseReList.value = null;
-    getSvSendCtrl();
-    smvCtrlblockRe();
-    getGooseReList();
+    if (newValue != null) {
+      getSvSendCtrl();
+      smvCtrlblockRe();
+      getGooseReList();
+    }
   }
 );
 watch(

+ 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: 272000075,
+    scd_id: 296000081,
     pagesize: 10000,
     name: "SubNetwork",
   });
@@ -170,7 +170,7 @@ const allApData = ref({});
 const initLoad = async () => {
   allApData.value = {};
   const allAP = await nodeList({
-    scd_id: 272000075,
+    scd_id: 296000081,
     pagesize: 10000,
     name: "ConnectedAP",
   });
@@ -198,7 +198,7 @@ const initLoad = async () => {
 };
 //处理重复的ip
 const ipNetaddr = async () => {
-  const ipRes = await iedNetaddr({ scd_id: 272000075 });
+  const ipRes = await iedNetaddr({ scd_id: 296000081 });
   if (ipRes.code == 1) {
     return;
   }
@@ -500,11 +500,11 @@ const clickNetworkInfo = (value) => {
 
 const iedRelationData = ref([]);
 const iedRelation = async () => {
-  const iedRes = await scdIedRelation({ scd_id: 272000075 });
+  const iedRes = await scdIedRelation({ scd_id: 296000081 });
   iedRelationData.value = iedRes.data;
 };
 //弹窗=============
-provide('scdId',272000075)
+provide('scdId',296000081)
 </script>
   
 <style scoped lang="scss">

+ 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: 272000075,
+    scd_id: 296000081,
   },
 });
 const loading = ref(true);
@@ -171,7 +171,7 @@ const { queryParams } = toRefs(data);
 // 表单重置
 const reset = () => {
   queryParams.value = {
-    scd_id: 272000075,
+    scd_id: 296000081,
     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: 272000075 });
+  const areaRes = await areaList({ scd_id: 296000081 });
   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: 272000075 });
+  const typeRes = await iedTypelist({ scd_id: 296000081 });
   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: 272000075 });
+  const iedRes = await scdIedRelation({ scd_id: 296000081 });
   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: 272000075, area_id: value };
+    const mainClick = { scd_id: 296000081, area_id: value };
     const forms = mainValue ? mainClick : queryParams.value;
     open.value = true;
     dialogData.value = [];