relationShip.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. <!-- 装置关联关系 -->
  2. <template>
  3. <div>
  4. <div class="main-cont" ref="myElement" id="treedom">
  5. <div class="main-left" ref="leftElement">
  6. <div
  7. v-for="(item, index) in leftList"
  8. :key="index"
  9. class="cont"
  10. @click="clickImg(item)"
  11. >
  12. <img
  13. :src="devicePng"
  14. alt=""
  15. class="img-item"
  16. :ref="(el) => setdom(el, item)"
  17. />
  18. <div>{{ item.ref_ied_name }}</div>
  19. <div class="ied-desc">{{ item.ref_ied_desc }}</div>
  20. </div>
  21. </div>
  22. <div class="main-middle">
  23. <div class="middle-item" ref="middleElement">
  24. <img :src="devicePng" alt="" id="end" />
  25. <div v-if="listData">{{ listData.ied_name }}</div>
  26. <div v-if="listData">{{ listData.desc }}</div>
  27. </div>
  28. </div>
  29. <div class="main-right" ref="rightElement">
  30. <div
  31. v-for="(item, index) in rightList"
  32. :key="index"
  33. class="cont"
  34. @click="clickImg(item)"
  35. >
  36. <img
  37. :src="devicePng"
  38. alt=""
  39. class="img-item"
  40. :ref="(el) => setdomRight(el, item)"
  41. />
  42. <div>{{ item.ref_ied_name }}</div>
  43. <div class="ied-desc">{{ item.ref_ied_desc }}</div>
  44. </div>
  45. </div>
  46. </div>
  47. <div id="wrapper"></div>
  48. </div>
  49. </template>
  50. <script setup>
  51. import { onMounted, watch, ref, nextTick, defineEmits } from "vue";
  52. import devicePng from "@/assets/image/instruct/device.png";
  53. import LeaderLine from "../../../../public/leader-line.min.js";
  54. import AnimEvent from "../../../../public/anim-event.min.js";
  55. const props = defineProps({
  56. checkData: {
  57. type: Object,
  58. default: () => {},
  59. },
  60. isOpen: {
  61. type: Boolean,
  62. default: false,
  63. },
  64. iedRelation: {
  65. type: Object,
  66. default: () => {},
  67. },
  68. tabName: {
  69. type: String,
  70. default: "",
  71. },
  72. });
  73. const middleElement = ref(null);
  74. const rightElement = ref(null);
  75. const leftElement = ref(null);
  76. const myElement = ref(null);
  77. let leaderLines = ref([]); //控制线条显示
  78. const leftList = ref([]);
  79. const rightList = ref([]);
  80. const domList = ref(new Map()); //获取 所有的ref
  81. const domListRight = ref(new Map()); //获取 所有的ref
  82. const listData = ref(props.checkData); //线条左右两侧的数据
  83. const emit = defineEmits(["result"]); //如果不加这个再次点击左侧会没有反应
  84. const setdom = (el, item) => {
  85. //左侧dom
  86. if (el) {
  87. domList.value.set(item, el);
  88. }
  89. };
  90. const setdomRight = (el, item) => {
  91. //右侧dom
  92. if (el) {
  93. domListRight.value.set(item, el);
  94. }
  95. };
  96. const processArray = (arr) => {
  97. // ref_ied_id作为键,obj作为值
  98. const uniqueObjects = new Map();
  99. // 遍历数组
  100. for (const obj of arr) {
  101. const { ref_ied_id } = obj;
  102. // 如果当前对象的 ref_ied_id 属性已经存在于 uniqueObjects 中
  103. if (uniqueObjects.has(ref_ied_id)) {
  104. // 将对应对象的 ref_type 属性设为 2,箭头双向
  105. uniqueObjects.get(ref_ied_id).ref_type = 2;
  106. } else {
  107. // 否则,将当前对象添加到 uniqueObjects 中
  108. uniqueObjects.set(ref_ied_id, obj);
  109. }
  110. }
  111. // 将 uniqueObjects 中的值转为数组并返回
  112. return Array.from(uniqueObjects.values());
  113. };
  114. //点击图片的时候筛选出数据
  115. const clickImg = (dataItem) => {
  116. Object.values(props.iedRelation).find((item) => {
  117. if (item.ied_name == dataItem.ref_ied_name) {
  118. listData.value = item;
  119. }
  120. });
  121. };
  122. watch(
  123. () => props.checkData,
  124. (newValue, oldV) => {
  125. listData.value = newValue;
  126. if (newValue && leaderLines.value.length > 0) {
  127. // leaderLines.value.forEach((line) => line.remove()); //清除连线
  128. leaderLines.value = [];
  129. }
  130. },
  131. { deep: true }
  132. );
  133. watch(
  134. () => listData.value,
  135. (newValue) => {
  136. emit("result", newValue);
  137. clickResetLine();
  138. }
  139. );
  140. watch(
  141. () => props.isOpen,
  142. (newValue) => {
  143. if (newValue) {
  144. domList.value.clear();
  145. domListRight.value.clear();
  146. leaderLines.value = [];
  147. nextTick(() => {
  148. // middleLinePosition();
  149. removeLine();
  150. });
  151. }
  152. }
  153. );
  154. //点击后重置数据和线条
  155. const clickResetLine = () => {
  156. domList.value.clear();
  157. domListRight.value.clear();
  158. leaderLines.value = [];
  159. removeLine();
  160. middleLinePosition();
  161. setLine();
  162. };
  163. // 将设备列表分成两份
  164. const bothSide = (data) => {
  165. const formatArr = processArray(data);
  166. const arrlenght = formatArr.length;
  167. const long1 = Math.ceil(arrlenght / 2);
  168. leftList.value = formatArr.splice(0, long1);
  169. rightList.value = formatArr.splice(0);
  170. };
  171. const setLeaderline = () => {
  172. // lineArr.value = [];
  173. //线条样式
  174. const lineStyle0 = {
  175. color: "#51637F",
  176. size: 2,
  177. path: "straight",
  178. startPlug: "arrow1",
  179. endPlug: "behind",
  180. startSocket: "right",
  181. endSocket: "left",
  182. };
  183. const lineStyle1 = {
  184. color: "#51637F",
  185. size: 2,
  186. path: "straight",
  187. endPlug: "arrow1",
  188. startSocket: "right",
  189. endSocket: "left",
  190. };
  191. const lineStyle2 = {
  192. color: "#134BEA",
  193. size: 2,
  194. path: "straight",
  195. startPlug: "arrow1",
  196. endPlug: "arrow1",
  197. startSocket: "right",
  198. endSocket: "left",
  199. };
  200. const lineStyleRight0 = {
  201. color: "#51637F",
  202. size: 2,
  203. path: "straight",
  204. startPlug: "arrow1",
  205. endPlug: "behind",
  206. startSocket: "left",
  207. endSocket: "right",
  208. };
  209. const lineStyleRight1 = {
  210. color: "#51637F",
  211. size: 2,
  212. path: "straight",
  213. endPlug: "arrow1",
  214. startSocket: "left",
  215. endSocket: "right",
  216. };
  217. const lineStyleRight2 = {
  218. color: "#134BEA",
  219. size: 2,
  220. path: "straight",
  221. startPlug: "arrow1",
  222. endPlug: "arrow1",
  223. startSocket: "left",
  224. endSocket: "right",
  225. };
  226. const startDom = document.getElementById("end");
  227. let count = 56;
  228. //循环画线
  229. for (const [key, value] of domList.value) {
  230. const endDom = value;
  231. let line;
  232. count += 5;
  233. if (key.ref_type == 0) {
  234. line = new LeaderLine(
  235. endDom,
  236. LeaderLine.pointAnchor(startDom, { x: 0, y: count }),
  237. lineStyle0
  238. );
  239. } else if (key.ref_type == 1) {
  240. line = new LeaderLine(
  241. endDom,
  242. LeaderLine.pointAnchor(startDom, { x: 0, y: count }),
  243. lineStyle1
  244. );
  245. } else if (key.ref_type == 2) {
  246. line = new LeaderLine(
  247. endDom,
  248. LeaderLine.pointAnchor(startDom, { x: 0, y: count }),
  249. lineStyle2
  250. );
  251. }
  252. // 保存进数组,方便进行遍历删除
  253. leaderLines.value.push(line);
  254. }
  255. let count2 = 56;
  256. //循环画线右侧
  257. for (const [key, value] of domListRight.value) {
  258. const endDom = value;
  259. let line2;
  260. count2 += 5;
  261. if (key.ref_type == 0) {
  262. line2 = new LeaderLine(
  263. endDom,
  264. LeaderLine.pointAnchor(startDom, { x: "100%", y: count2 }),
  265. lineStyleRight0
  266. );
  267. } else if (key.ref_type == 1) {
  268. line2 = new LeaderLine(
  269. endDom,
  270. LeaderLine.pointAnchor(startDom, { x: "100%", y: count2 }),
  271. lineStyleRight1
  272. );
  273. } else if (key.ref_type == 2) {
  274. line2 = new LeaderLine(
  275. endDom,
  276. LeaderLine.pointAnchor(startDom, { x: "100%", y: count2 }),
  277. lineStyleRight2
  278. );
  279. }
  280. // 保存进数组,方便进行遍历删除
  281. leaderLines.value.push(line2);
  282. }
  283. hiddenLine();
  284. };
  285. //设置中间盒子的所在位置
  286. const middleLinePosition = () => {
  287. setTimeout(() => {
  288. const heights = myElement.value.scrollHeight;
  289. const leftListLength = leftList.value.length;
  290. const rightListLength = rightList.value.length;
  291. const setElementMarginTop = (element, value) => {
  292. element.value.style.marginTop = `${value}px`;
  293. };
  294. if (leftList.value.length > 3 || rightList.value.length > 3) {
  295. middleElement.value.style.marginTop = `${(heights - 60) / 2}px`; // 设置元素的垂直位置
  296. } else {
  297. setElementMarginTop(leftElement, 0);
  298. setElementMarginTop(rightElement, 0);
  299. setElementMarginTop(middleElement, 150);
  300. }
  301. if (rightListLength == 1) {
  302. setElementMarginTop(rightElement, 165);
  303. }
  304. if (leftListLength == 1) {
  305. setElementMarginTop(leftElement, 165);
  306. }
  307. }, 0);
  308. };
  309. onMounted(() => {
  310. nextTick(() => {
  311. setLine();
  312. middleLinePosition();
  313. nextTick(() => {
  314. newPositionLine();
  315. });
  316. });
  317. });
  318. //滚动时重定位线条
  319. const newPositionLine = () => {
  320. document.getElementById("treedom").addEventListener(
  321. "scroll",
  322. AnimEvent.add(() => {
  323. leaderLines.value.forEach((line) => {
  324. hiddenLine();
  325. line.position();
  326. line.positionByWindowResize = false;
  327. });
  328. }),
  329. false
  330. );
  331. window.addEventListener(
  332. "resize",
  333. AnimEvent.add(function () {
  334. leaderLines.value.forEach((line) => {
  335. hiddenLine();
  336. line.position();
  337. line.positionByWindowResize = false;
  338. });
  339. }),
  340. false
  341. );
  342. };
  343. //弹窗打开后使得线条在指定区域中
  344. const hiddenLine = () => {
  345. const elmWrapper = document.getElementById("wrapper");
  346. // 移动 line
  347. document.body.querySelectorAll("body .leader-line").forEach((node) => {
  348. elmWrapper.appendChild(node);
  349. });
  350. elmWrapper.style.transform = "none";
  351. var rectWrapper = elmWrapper.getBoundingClientRect();
  352. // Move to the origin of coordinates as the document
  353. elmWrapper.style.transform = `translate(${
  354. (rectWrapper.left + window.scrollY) * -1
  355. }px, ${(rectWrapper.top + window.scrollX) * -1}px)`;
  356. };
  357. const setLine = () => {
  358. if (listData.value) {
  359. bothSide(listData.value.list);
  360. }
  361. setTimeout(() => {
  362. setLeaderline();
  363. }, 300);
  364. };
  365. const removeLine = () => {
  366. const elmWrapper = document.getElementById("wrapper");
  367. if (elmWrapper) {
  368. document.body.querySelectorAll("body .leader-line").forEach((node) => {
  369. elmWrapper.removeChild(node);
  370. });
  371. }
  372. };
  373. </script>
  374. <style lang="scss">
  375. @mixin img-size {
  376. width: 150px;
  377. height: 90px;
  378. margin-bottom: 10px;
  379. }
  380. @mixin left-and-right {
  381. display: flex;
  382. flex-direction: column;
  383. }
  384. .main-cont {
  385. margin-top: 60px;
  386. display: flex;
  387. justify-content: space-evenly;
  388. }
  389. .leader-line {
  390. z-index: 3000;
  391. }
  392. .main-left {
  393. display: flex;
  394. @include left-and-right;
  395. .img-item {
  396. @include img-size;
  397. }
  398. }
  399. .main-middle {
  400. box-sizing: border-box;
  401. img {
  402. margin-bottom: 10px;
  403. }
  404. .middle-item {
  405. @include left-and-right;
  406. align-items: center;
  407. color: #ffcb11;
  408. }
  409. }
  410. .main-right {
  411. display: flex;
  412. @include left-and-right;
  413. .img-item {
  414. @include img-size;
  415. }
  416. }
  417. .cont {
  418. @include left-and-right;
  419. align-items: center;
  420. margin-bottom: 10px;
  421. .ied-desc {
  422. color: #255ce7;
  423. }
  424. }
  425. #wrapper {
  426. width: 0;
  427. height: 0;
  428. position: relative; /* Origin of coordinates for lines, and scrolled content (i.e. not `absolute`) */
  429. }
  430. </style>