<template>
<div class="dev-linkage-diagram">
<div class="header">
<h3>设备联动图- {{ title }}</h3>
<div class="btnBox">
<van-button type="info" @click="$router.back()">全部</van-button>
<van-button type="info" class="back-btn" @click="$router.back()">返回</van-button>
</div>
</div>
<!-- 关联图 -->
<div class="graphRefBox">
<RelationGraph ref="graphRef" :options="graphOptions">
<template #node="{ node }">
<p @click="sss">555</p>
<!-- 当前设备关联 -->
<div class="nodeBox" v-if="viewModes == 1">
<div :class="['icon', node.data.className]" @click.stop="onNodeClick(node)"></div>
<div class="popoverBox"
v-if="activateData.type == node.data.type && activateData.id == node.data.id">
<div class="top">
<div class="num">
{{ node.data.pbId }}
</div>
<span>{{ node.data.type }}</span>
<div :class="{ 'state': true, 'online': node.data.online }">
{{ node.data.online ? "在线" : "离线" }}
</div>
</div>
<p>{{ node.data.location || "未设置" }}</p>
<div class="popover-btn">
<van-button plain round size="small" type="info" :disabled="viewModes == 1">单点</van-button>
<van-button plain round size="small" type="info" :disabled="viewModes == 2"
@click.native="viewSingle(node.data, 2)">区域</van-button>
</div>
</div>
</div>
<!-- 区域关联 -->
</template>
</RelationGraph>
</div>
</div>
</template>
<script>
import lodash from 'lodash'
import {
getPartRelation,
getSlightRelation,
getIopRelation,
getAreaRelation,
getAreaList,
} from "@/api/defense";
import RelationGraph from "relation-graph";
export default {
name: "DevLinkageDiagram",
components: { RelationGraph },
props: {
devId: {
type: String,
required: true
},
devType: {
type: String,
required: true,
},
model: {
type: String,
required: true,
},
diagramData: {
type: Object,
default: () => { },
},
viewMode: {
type: Number,
default: 1, //type: 1设备 2区域 3全部
}
},
data() {
return {
viewModes: lodash.cloneDeep(this.viewMode),
// 前端关系图推荐-relation-graph
graphOptions: {
allowShowDownloadButton: false, //【下载图片】按钮
defaultFocusRootNode: false, //根节点添加一个被选中的样式
disableNodeClickEffect: true, //是否禁用节点默认的点击效果
disableLineClickEffect: true, //是否禁用线默认的点击效果
defaultShowLineLabel: false, //是否显示连线文字
layout: {
layoutName: "tree",
from: "left",
min_per_width: 200,
min_per_height: 200,
},
defaultNodeColor: "rgba(255, 255, 255, 0)",
},
nodes: [],
lines: [],
rootId: "rootId",
activateData: {
type: '',
id: '',
}
}
},
computed: {
title() {
if (this.viewModes == 1) {
return "设备"
} else if (this.viewModes == 2) {
return "区域"
} else {
return "全部"
}
}
},
created() {
},
mounted() {
if (this.diagramData && this.diagramData.devId && this.viewModes == 1) {
this.getRelation(this.diagramData);
} else if (this.diagramData && this.diagramData.devId && this.viewModes == 2) {
this.getAreaRelation(this.diagramData);
}
},
methods: {
async getRelation(data) {
this.nodes = [];
this.lines = [];
this.viewModes = 1;
const nodeClassName = this.mapClassName(data.rowDevType);
const rootNode = {
id: this.rootId,
data: {
className: nodeClassName,
type: data.rowDevType,
online: data.online,
areaId: data.areaId,
location: data.rowPbName,
pbId: data.id,
},
};
this.nodes.push(rootNode);
let res = {};
if (data.activeTab === "声光") {
res = await getSlightRelation(data);
this.slightMapData(res);
} else if (data.activeTab === "输出模块") {
res = await getIopRelation(data);
this.iopMapData(res);
} else {
res = await getPartRelation(data);
this.partsMapData(res);
}
this.initGraph();
},
initGraph() {
const jsonData = {
rootId: this.rootId,
nodes: this.nodes,
lines: this.lines,
};
// 以上数据中的node和link可以参考"Node节点"和"Link关系"中的参数进行配置
this.$refs.graphRef.setJsonData(jsonData);
},
mapClassName(e) {
let className = "";
if (e === "烟感" || e === "烟温") {
className = "yg";
} else if (e === "声光") {
className = "sg";
} else if (e === "输出模块") {
className = "sc";
} else if (e === "手报") {
className = "sb";
}
return className;
},
partsMapData(res) {
this.graphSlightData('parts', res.iops, "iops");
this.graphSlightData('parts', res.slights, "slights", "top");
},
slightMapData(res) {
this.graphSlightData('slight', res.parts, "parts", "top");
this.graphSlightData('slight', res.iops, "iops");
},
iopMapData(res) {
this.graphSlightData('iops', res.parts, "parts", "top");
this.graphSlightData('iops', res.slights, "slights");
},
/**
* 配件/声光/输出模块关联数据处理
* @param {*} types 类型
* @param {*} res 原数据
* @param {*} from 方向
* @param {*} idPrefix 前缀
*/
graphSlightData(types, res, idPrefix, from = "bottom") {
res.forEach((e) => {
const className = this.mapClassName(e.inType || e.pbType);
const id = idPrefix == 'parts' ? idPrefix + e.pbId : idPrefix + e.inId;
const pbId = idPrefix == 'parts' ? e.pbId : e.inId;
const type = idPrefix == 'parts' ? e.pbType : e.inType;
const location = idPrefix == 'parts' ? e.pbName : e.inName;
const data = {
id,
data: {
className,
type,
online: e.online,
areaId: e.areaId,
pbId,
location,
},
};
this.nodes.push(data);
if (from == "bottom") {
this.lines.push({ from: this.rootId, to: id });
} else if (types == 'parts') {
this.lines.push({
from: id,
to: this.rootId,
showStartArrow: true,
showEndArrow: false,
});
} else {
this.lines.push({
from: id,
to: this.rootId,
showStartArrow: types == 'iops',
showEndArrow: types != 'iops',
});
}
});
},
// 单个区域处理
async getAreaRelation(data) {
const res = await getAreaRelation(data);
if (res.parts.length && res.slights.length && res.iops.length) {
this.nodes = [];
this.lines = [];
this.regionalDataProcessing(res, this.rootId, 'slightId', 'iopId')
this.viewModes = 2;
this.initGraph();
} else {
this.$message({
message: '该区域下无关联设备',
type: 'warning',
})
}
},
// 全部区域处理
async getAreaList() {
const params = {
devId: this.devId,
devType: this.devType,
model: this.model,
}
const res = await getAreaList(params);
this.viewModes = 3;
let nodeList = [];
let lineList = [];
res.forEach((e, index) => {
if (e.parts.length && e.slights.length && e.iops.length) {
this.regionalDataProcessing(e, this.rootId + index, 'slighId' + index, 'iopId' + index)
nodeList.push(...lodash.cloneDeep(this.nodes));
lineList.push(...lodash.cloneDeep(this.lines));
}
})
this.nodes = nodeList;
this.lines = lineList;
this.initGraph();
},
regionalDataProcessing(res, rootId = this.rootId, slightId = 'slightId', iopId = 'iopId') {
this.nodes = [];
this.lines = [];
this.graphSlightData('parts', res.parts, "parts");
const partsList = lodash.cloneDeep(this.nodes);
this.nodes = [];
this.graphSlightData('slight', res.slights, "slight");
const slightList = lodash.cloneDeep(this.nodes);
this.nodes = [];
this.graphSlightData('iops', res.iops, "iops");
const iopList = lodash.cloneDeep(this.nodes);
this.nodes = [
{
id: rootId,
data: partsList,
},
{
id: slightId,
data: slightList
},
{
id: iopId,
data: iopList
},
];
this.lines = [
{
from: slightId,
to: rootId,
showStartArrow: true,
showEndArrow: false,
},
{
from: rootId,
to: iopId,
}
]
},
// 按钮点击
viewSingle(nodeData, type) {
if (type == 1) {
const data = {
devId: this.devId,
devType: this.devType,
model: this.model,
areaId: nodeData.data.areaId,
id: nodeData.data.pbId,
online: nodeData.data.online,
rowDevType: nodeData.data.type,
rowPbName: nodeData.data.location,
activeTab: nodeData.data.type,
}
this.getRelation(data);
} else if (type == 2) {
const data = {
devId: this.devId,
devType: this.devType,
model: this.model,
areaId: nodeData.areaId,
}
console.log(121212);
this.getAreaRelation(data)
}
},
getContainer(id) {
return document.querySelector(id);
},
onNodeClick(node) {
console.log(5555);
const data = this.nodes.find(item => item.id == node.id && item.type == node.type)
console.log(data);
// if (this.activateData.id === node.data.id && this.activateData.type === node.data.type) {
// this.activateData = {
// type: '',
// id: '',
// };
// } else {
// this.activateData = node.data;
// }
},
sss(){
console.log(5555);
}
},
};
</script>
<style scoped lang="less">
.dev-linkage-diagram {
position: relative;
height: 100vh;
.header {
position: absolute;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 40px;
width: 100%;
border-radius: 10px;
z-index: 1;
h3 {
font-size: 40px;
}
.btnBox {
.back-btn {
margin-left: 40px;
}
.van-button {
height: 80px;
}
}
}
.graphRefBox {
width: 100%;
height: 100%;
/deep/.rel-map-ready {
background-image: url('~@/assets/img/linkage/bg.png');
background-size: 100% 100%;
}
.listBox {}
.nodeBox {
position: relative;
}
.icon {
width: 100px;
height: 100px;
background-repeat: no-repeat;
background-size: 100% 100%;
}
.yg {
background-image: url('~@/assets/img/linkage/yg.png');
}
.sg {
background-image: url('~@/assets/img/linkage/sg.png');
}
.sc {
background-image: url('~@/assets/img/linkage/sc.png');
}
.sb {
background-image: url('~@/assets/img/linkage/sb.png');
}
.popoverBox {
position: absolute;
top: 120px;
left: 50%;
transform: translateX(-50%);
padding: 40px;
width: 580px;
border-radius: 40px;
background-color: #fff;
.top {
display: flex;
align-items: center;
.num {
width: 90px;
height: 90px;
background-color: #3786ff;
border-radius: 50%;
color: #fff;
font-size: 34px;
text-align: center;
line-height: 90px;
}
span {
margin: 0 14px;
color: #000;
}
.state {
padding: 6px 10px;
background-color: rgba(158, 151, 151, 0.8);
border-radius: 4px;
}
.online {
background-color: #1ddaa4;
}
}
p {
margin: 20px 0;
text-align: left;
color: #888;
}
.popover-btn {
.van-button {
padding: 10px 60px;
margin-right: 30px;
}
}
}
}
}
</style>
为什么sss事件没有用