123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634 |
- <!-- 虚端子关系 -->
- <template>
- <div class="main">
- <!-- 关联图 -->
- <div class="main-cont" id="treedom3">
- <div class="main-left">
- <div v-for="(item, index) in leftList" :key="index" class="conts">
- <div class="cont-title">
- <img :src="devicePng" alt="" class="img-item" />
- <div class="cont-item">
- <div>{{ item.descName }}</div>
- <div>{{ item.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" v-if="tagList">
- <div>{{ middleTitelDesc }}</div>
- <div>{{ tagList.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"
- :class="{
- 'del-middle': item.opt == 'd',
- 'upt-middle': item.opt == 'u',
- }"
- >
- {{ item.no }}
- </div>
- <div
- class="midlestyle"
- v-if="!item.isDecollate"
- :class="{
- 'add-middle': item.opt == 'i',
- 'del-middle': item.opt == 'd',
- 'upt-middle': item.opt == 'u',
- }"
- >
- <div class="middle-cont">
- <div v-if="item.inout_type == 'out'">
- <span v-if="item.attr_da_name">
- {{
- `${item.attr_ld_inst}/${item.attr_prefix}${item.attr_ln_class}${item.attr_ln_inst}.${item.attr_do_name}.${item.attr_da_name}`
- }}
- </span>
- <span v-else>
- {{
- `${item.attr_ld_inst}/${item.attr_prefix}${item.attr_ln_class}${item.attr_ln_inst}.${item.attr_do_name}`
- }}
- </span>
- </div>
- <div v-else-if="item.inout_type == 'in'">
- {{ item.attr_int_addr }}
- </div>
- <div v-if="item.inout_type == 'out'">
- {{ item.do_source_desc }}
- </div>
- <div v-else-if="item.inout_type == 'in'">
- {{ item.do_target_desc }}
- </div>
- </div>
- <div style="flex: 1">
- <img
- :src="addMiddlePng"
- alt=""
- class="img-item-middle"
- v-if="item.opt == 'i'"
- />
- <img
- :src="delMiddlePng"
- alt=""
- class="img-item-middle"
- v-else-if="item.opt == 'd'"
- />
- <img
- :src="uptMiddlePng"
- alt=""
- class="img-item-middle"
- v-else-if="item.opt == 'u'"
- />
- </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">
- <div class="cont-title">
- <img :src="devicePng" alt="" class="img-item" />
- <div class="cont-item">
- <div>{{ item.descName }}</div>
- <div>{{ item.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, inject } from "vue";
- import devicePng from "@/assets/image/instruct/device.png";
- import addMiddlePng from "@/assets/image/CID/add_middle.png";
- import delMiddlePng from "@/assets/image/CID/del_middle.png";
- import uptMiddlePng from "@/assets/image/CID/upt_middle.png";
- import LeaderLine from "../../../../public/leader-line.min.js";
- import AnimEvent from "../../../../public/anim-event.min.js";
- import { compResult } from "@/api/scdCheck/scdCheck2";
- import { scdIedRelation } from "@/api/iedNetwork";
- import { useRoute } from "vue-router";
- const route = useRoute();
- const props = defineProps({
- //对比结果scd所传参数
- clickRowDatas: {
- type: Object,
- default: () => {},
- },
- clickList: {
- type: Object,
- default: () => {},
- },
- clickCodeValue: {
- type: String,
- default: "",
- },
- OpensclTrue: {
- type: Boolean,
- default: false,
- },
- recordDelIedVer: {
- //删除的列表数据,从sclUpdate中获取的
- type: Array,
- default: () => [],
- },
- });
- const svInfo = ref(null);
- //处理两边的数据
- const processBoth = (list, svResInfo, inoutType) => {
- const iedNames = {};
- if(!list||!svResInfo.data) return;
- list.forEach(async (item, index) => {
- item.titleItems = [];
- svResInfo.data.forEach((key) => {
- if (key.ied_name == item.ied_name) {
- if (key.inout_type == inoutType) {
- item.titleItems.push(key);
- }
- }
- });
- let iedNames = {};
- //为了得到每个小板块的标题描述
- if (!iedNames[item.ied_name]) {
- iedNames[item.ied_name] = true;
- if (!item.isdel) {
- const iedRes = await scdIedRelation({
- scd_id: scdIdValue,
- ied_name: item.ied_name,
- reset: 1,
- });
- if(!item.ied_name||!iedRes.data[item.ied_name]) return;
- item.descName = iedRes.data[item.ied_name].desc;
- } else {
- const iedRes = await scdIedRelation({
- scd_id: props.clickRowDatas.target_id,
- ied_name: item.ied_name,
- reset: 1,
- });
- item.descName = iedRes.data[item.ied_name].desc;
- }
- }
- });
- };
- 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 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);
- }
- };
- let tagList = ref(props.clickList); //左侧更改的设备列表
- // //scd一致性对比===
- const clickRow = ref(props.clickRowDatas);
- // //对比文件:头部对比的单个数据
- watch(
- () => props.clickRowDatas,
- (newValue) => {
- if (newValue) {
- clickRow.value = newValue;
- }
- }
- );
- //点击装置列表的数据变化
- watch(
- () => props.clickList,
- (newValue) => {
- if (newValue) {
- tagList.value = newValue;
- clickResetLine3()
- getNetworkInfo3();
- }
- }
- );
- const cClickCode = ref(props.clickCodeValue); //点击侧边栏差异项的code
- watch(
- () => props.clickCodeValue,
- (newValue) => {
- if (newValue) {
- cClickCode.value = newValue;
- clickResetLine3();
- getNetworkInfo3();
- }
- }
- );
- // //scd一致性对比====
- const bothdataList = ref([]); //处理得到总的两边的数据
- const middleTitelDesc = ref('');//中间数据的标题
- //得到中间的子版块数据
- const getNetworkInfo3 = async () => {
- bothdataList.value = [];
- leftList.value = [];
- rightList.value = [];
- let svResInfo;
- //scd一致性对比====
- const ids = clickRow.value ? clickRow.value.id : "";
- const names = tagList.value ? tagList.value.ied_name : "";
- svResInfo = await compResult({
- comp_id: ids,
- ied_name: names,
- comptype: "u",
- itemcode: cClickCode.value,
- });
- const iedRes = await scdIedRelation({
- scd_id: scdIdValue,
- ied_name: names,
- reset: 1,
- });
- middleTitelDesc.value = iedRes.data?iedRes.data[names].desc:''
- //省略号===
- 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) return;
- svResInfo.data.sort(function (a, b) {
- return a.no - b.no; // 从小到大排序
- });
- //得到两边总的每个版块的ied_name名称
- const maps = new Map();
- svResInfo.data.forEach((item) => {
- const key = item.ied_name + item.inout_type; //输入输出都是需要的
- if (!maps.has(key)) {
- maps.set(key, true);
- if (item.inout_type == "in") {
- leftList.value.push(item);
- } else if (item.inout_type == "out") {
- rightList.value.push(item);
- }
- }
- });
- if(!props.recordDelIedVer) return;
- // recordDelIedVer 删除的全部数据
- props.recordDelIedVer.forEach((itemdel) => {
- svResInfo.data.forEach((item) => {
- if (itemdel.ied_name == item.ied_name) {
- item.isdel = 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 clickResetLine3 = () => {
- domListMiddle3.value.clear();
- domListLeftChild3.value.clear();
- domListRightChild3.value.clear();
- leaderLines3.value = [];
- removeLine3();
- setLine();
- };
- const setLeaderline = () => {
- //左侧子组件
- for (let [key, value] of domListMiddle3.value) {
- for (const [key2, value2] of domListLeftChild3.value) {
- const endDom = value2;
- LeaderLine.positionByWindowResize = false;
- 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(() => {
- if(!leaderLines3.value) return;
- leaderLines3.value.forEach((line) => {
- if (line) {
- hiddenLine();
- line.position();
- line.positionByWindowResize = false;
- }
- });
- //中间展示图片的
- }),
- false
- );
- document.getElementById("treedom3").addEventListener(
- "resize",
- AnimEvent.add(function () {
- if(!diffline) return;
- diffline.forEach((line) => {
- hiddenLine();
- line.position();
- line.positionByWindowResize = false;
- });
- }),
- false
- );
- };
- //弹窗打开后使得线条在指定区域中
- const hiddenLine = () => {
- const elmWrapper = document.getElementById("wrapper");
- if(!elmWrapper) return;
- // 移动 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 = () => {
- setTimeout(() => {
- setLeaderline();
- newPositionLine();
- }, 500);
- };
- const removeLine3 = () => {
- leaderLines3.value = [];
- const elmWrapper = document.getElementById("wrapper");
- if (elmWrapper) {
- document.body.querySelectorAll("#wrapper .leader-line").forEach((node) => {
- elmWrapper.removeChild(node);
- });
- }
- };
- let scdIdValue = "";
- onMounted(() => {
- scdIdValue = route.query.id;
- getNetworkInfo3();
- //不加条件切换下方tab时会出现bug
- nextTick(() => {
- setLine();
- });
- });
- watch(
- () => props.OpensclTrue,
- (newValue) => {
- if (newValue) {
- domListMiddle3.value.clear();
- domListLeftChild3.value.clear();
- domListRightChild3.value.clear();
- leaderLines3.value = [];
- }
- nextTick(() => {
- removeLine3();
- });
- }
- );
- </script>
- <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;
- margin-top: 60px;
- overflow-y: auto;
- }
- .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,
- .main-left {
- 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;
- flex-direction: row;
- justify-content: center;
- align-items: center;
- border-radius: 2px;
- padding: 5px;
- color: #1a2447;
- flex: 1;
- .img-item-middle {
- width: 22px;
- height: 22px;
- }
- .middle-cont {
- display: flex;
- flex-direction: column;
- align-items: center;
- width: 90%;
- }
- }
- .del-middle {
- color: #e50505;
- }
- .upt-middle {
- color: #ffa011;
- }
- .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>
|