文章篇幅可能过长,注重讲解想要的效果图属性怎么配置,动态请求后端接口数据的渲染,以及点击节点或者节点展开按钮追加数据逻辑。如想直接实现效果图,可直接拉取全部代码标题中的代码,跟后端沟通返回数据格式,更改接口即可,文章最后还有优化代码,会比效果图更加完善,也可自行翻阅。
官方文档地址:http://www.relation-graph.com/#/docs/start
初始化关系图时需要配置属性,参考文档api
npm install --save relation-graph
<div class="legalperson">
<div style="height: calc(100vh - 50px)" v-loading="relationLoading">
<SeeksRelationGraph
ref="seeksRelationGraph"
:options="graphOptions"
:on-node-click="onNodeClick"
:on-line-click="onLineClick"
:on-node-expand="onNodeExpand"
/>
div>
div>
<script>
import SeeksRelationGraph from "relation-graph"; //引入插件
import { query_tree1 } from "@/api/affiliated"; //接口api
export default {
name: "legalperson",
components: { SeeksRelationGraph },
data() {
return {
relationLoading: false,
currentCase: "双向树",
//关系图属性配置(重要)
graphOptions: {
layouts: [
{
label: "中心",
layoutName: "tree", //布局方式(tree树状布局/center中心布局/force自动布局)
layoutClassName: "seeks-layout-center", //当使用这个布局时,会将此样式添加到图谱上
defaultJunctionPoint: "border", //默认的连线与节点接触的方式
defaultNodeShape: 0, //默认的节点形状,0:圆形;1:矩形
defaultLineShape: 1, //默认的线条样式(1:直线/2:样式2/3:样式3/4:折线/5:样式5/6:样式6)
centerOffset_y: 130, //根节点y坐标偏移量(针对项目配置单位为px)
min_per_width: 150, //节点距离限制:节点之间横向距离最小值
min_per_height: 180, //节点距离限制:节点之间纵向距离最小值
},
],
defaultNodeShape: 1, //默认的节点形状,0:圆形;1:矩形
defaultExpandHolderPosition: "bottom", //节点展开关闭的按钮位置
defaultLineShape: 4, //默认的线条样式(1:直线/2:样式2/3:样式3/4:折线/5:样式5/6:样式6)
defaultJunctionPoint: "tb", //默认的连线与节点接触的方式(border:边缘/ltrb:上下左右/tb:上下/lr:左右)当布局为树状布局时应使用tb或者lr,这样才会好看
defaultNodeBorderWidth: 0.2, //节点边框粗细
defaultLineColor: "rgba(0, 186, 189, 1)", //默认的线条颜色
defaultNodeColor: "rgba(0, 206, 209, 1)", //默认的节点背景颜色
defaultNodeWidth: "130", //节点宽度
defaultNodeHeight: "80", //节点高度
defaultFocusRootNode: false, //默认为根节点添加一个被选中的样式
moveToCenterWhenResize: false, //当图谱的大小发生变化时,是否重新让图谱的内容看起来居中
},
__graph_json_data: {}, //节点的数据
};
},
mounted() {
this.showSeeksGraph();
},
methods: {
//初始化节点数据
showSeeksGraph(query) {
this.__graph_json_data = {
rootId: "0", //根节点唯一id
nodes: [
{
id: "0",
offset_x: -59, //根节点位置偏移量
text: "XXXXXX股份有限公司", //节点文本
width: 250, //根节点宽度
color: "#0084ff", //根节点背景色
fontColor: "#ffffff", //根节点字体颜色
childrenLoaded: true, //配置标识,点击展开节点按钮时判断,不需要重复请求接口(懒加载数据)
},
//也可以在此添加静态子节点(展示效果图)
{
id: "101",
text: "节点1",
expandHolderPosition: "top", //展开节点的显示位置,查看后续效果图
expanded: false,
childrenLoaded: false, //节点重复展开标识
},
{
id: "102",
text: "节点2",
expandHolderPosition: "bottom",
expanded: false,
childrenLoaded: false, //节点重复展开标识
},
],
links: [
//根节点上方侧的关系数据,注意关系的方向,是从 节点--->根节点:
{ from: "101", to: "0" }, //上方(从子节点出发到根节点)
{ from: "0", to: "102" }, //下方(从根节点出发到子节点)
],
};
//将根节点渲染进图谱中
this.$refs.seeksRelationGraph.setJsonData(this.__graph_json_data,(seeksRGGraph) => {
this.shareholdersData();
}
);
},
},
};
</script>
动态获取接口数据渲染
可以看到结构树需要的数据里属性必须有自己的要求,所以公司想要实现案例本就没太多的可选性,所以只能希望后端配合自己做返回的数据处理,如果把数据处理都放在前端的话,性能要求可能会有问题,不建议此操作。
重复代码就不多写了,直接写请求接口获取渲染数据的方法,属性配置不变
mounted() {
this.showSeeksGraph();
},
methods: {
//加载根节点数据后请求接口
showSeeksGraph(query) {
this.__graph_json_data = {
rootId: "0",
nodes: [
{
id: "0",
offset_x: -59,
text: "XXXXXX股份有限公司",
width: 250,
color: "#0084ff",
fontColor: "#ffffff",
childrenLoaded: false,
},
// { id: "102", text: "对外投资", color: '#83D942', expandHolderPosition: 'bottom', expanded: false, childrenLoaded: false, },
// { id: "101", text: "股东",},
],
links: [
// { from: "101", to: "0", color: "#6d9bfd", },
],
};
//setJsonData初始化数据方法
this.$refs.seeksRelationGraph.setJsonData(this.__graph_json_data,(seeksRGGraph) => {
this.shareholdersData();
}
);
},
//加载接口数据
shareholdersData() {
let dictionary = {
parentcodeName: "",
relatedtypedetail: "101",
};
query_tree1(dictionary).then((res) => {
this.__graph_json_data.nodes = res.data.nodes;
this.__graph_json_data.links = res.data.links;
//找到节点appendJsonData方法是追加数据
this.$refs.seeksRelationGraph.appendJsonData(this.__graph_json_data,(seeksRGGraph) => {}
);
});
},
//点击展开节点按钮触发
onNodeExpand(node, e) {
this.additionalData(node);
},
//像图谱中追加数据
additionalData(info) {
let dictionary = {
parentcodeName: info.text,
parentcode: info.id,
};
//判断是否已经动态加载数据了
if (info.data.childrenLoaded) {
console.log("这个节点的子节点已经加载过了");
// this.$refs.seeksRelationGraph.refresh() //可以看官方文档,此方法可以刷新布局
return;
}
info.data.expanded = true;
info.data.childrenLoaded = true;
query_tree1(dictionary)
.then((res) => {
this.__graph_json_data.nodes = res.data.nodes;
this.__graph_json_data.links = res.data.links;
// this.$refs.seeksRelationGraph.refresh()
this.$refs.seeksRelationGraph.appendJsonData(this.__graph_json_data,(seeksRGGraph) => {
}
);
})
},
onNodeClick() {},
onLineClick() {},
},
<template>
<div class="legalperson">
<div style="height: calc(100vh - 50px)" v-loading="relationLoading">
<SeeksRelationGraph
ref="seeksRelationGraph"
:options="graphOptions"
:on-node-click="onNodeClick"
:on-line-click="onLineClick"
:on-node-expand="onNodeExpand"
/>
</div>
</div>
</template>
<script>
import SeeksRelationGraph from "relation-graph";
import { query_tree1 } from "@/api/affiliated";
export default {
name: "legalperson",
components: { SeeksRelationGraph },
data() {
return {
dialogapproval: false,
relationLoading: false,
dialogLoading: false,
currentCase: "双向树",
isShowCodePanel: false,
graphOptions: {
layouts: [
{
label: "中心",
layoutName: "tree",
layoutClassName: "seeks-layout-center",
defaultJunctionPoint: "border",
defaultNodeShape: 0,
defaultLineShape: 1,
// min_per_height: "100",
// centerOffset_x: -300,
centerOffset_y: 130,
min_per_width: 150,
min_per_height: 180,
},
],
defaultNodeShape: 1,
defaultExpandHolderPosition: "bottom", //节点展开关闭的按钮位置
defaultLineShape: 4,
defaultJunctionPoint: "tb", //
defaultNodeBorderWidth: 0.2, //节点边框粗细
defaultLineColor: "rgba(0, 186, 189, 1)",
defaultNodeColor: "rgba(0, 206, 209, 1)",
defaultNodeWidth: "130", //节点宽度
defaultNodeHeight: "80", //节点高度
defaultFocusRootNode: false,
moveToCenterWhenResize: false,
},
__graph_json_data: {},
};
},
mounted() {
this.showSeeksGraph();
},
methods: {
//查询中煤法人查询
showSeeksGraph(query) {
this.__graph_json_data = {
rootId: "0",
nodes: [
{
id: "0",
offset_x: -59,
text: "XXXXXXX股份有限公司",
width: 250,
color: "#0084ff",
fontColor: "#ffffff",
childrenLoaded: false, //节点展开关闭标识
},
// { id: "102", text: "对外投资", color: '#83D942', expandHolderPosition: 'bottom', expanded: false, childrenLoaded: false, },
// { id: "101", text: "股东",},
],
links: [
// { from: "101", to: "0", color: "#6d9bfd", },
],
};
this.$refs.seeksRelationGraph.setJsonData(
this.__graph_json_data,
(seeksRGGraph) => {
this.shareholdersData();
}
);
},
//加载股东数据
shareholdersData() {
this.relationLoading = true;
let dictionary = {
parentcodeName: "XXXXXX股份有限公司",
relatedtypedetail: "101",
};
query_tree1(dictionary)
.then((res) => {
this.__graph_json_data.nodes = res.data.nodes;
this.__graph_json_data.links = res.data.links;
this.$refs.seeksRelationGraph.appendJsonData(
this.__graph_json_data,
(seeksRGGraph) => {
this.foreignData();
}
);
})
.catch(() => {
this.relationLoading = false;
});
},
//加载对外投资数据
foreignData() {
let dictionary = {
parentcodeName: "XXXXXX股份有限公司",
relatedtypedetail: "102",
};
query_tree1(dictionary)
.then((res) => {
this.__graph_json_data.nodes = res.data.nodes;
this.__graph_json_data.links = res.data.links;
// this.$refs.seeksRelationGraph.refresh()
this.$refs.seeksRelationGraph.appendJsonData(
this.__graph_json_data,
(seeksRGGraph) => {
this.relationLoading = false;
}
);
})
.catch(() => {
this.relationLoading = false;
});
},
//点击节点触发
onNodeClick(node, $event) {},
//点击展开节点按钮触发
onNodeExpand(node, e) {
this.additionalData(node);
},
//向图谱中追加数据
additionalData(info) {
let dictionary = {
parentcodeName: info.text,
parentcode: info.id,
};
//点击根节点上,下请求入参不同
if (info.expandHolderPosition == "top") {
dictionary.relatedtypedetail = "101";
} else if (info.expandHolderPosition == "bottom") {
dictionary.relatedtypedetail = "102";
}
//判断是否已经动态加载数据了
if (info.data.childrenLoaded) {
console.log("这个节点的子节点已经加载过了");
// this.$refs.seeksRelationGraph.refresh()
return;
}
info.data.expanded = true;
info.data.childrenLoaded = true;
this.relationLoading = true;
query_tree1(dictionary)
.then((res) => {
this.__graph_json_data.nodes = res.data.nodes;
this.__graph_json_data.links = res.data.links;
// this.$refs.seeksRelationGraph.refresh()
this.$refs.seeksRelationGraph.appendJsonData(
this.__graph_json_data,
(seeksRGGraph) => {
this.relationLoading = false;
}
);
})
.catch(() => {
this.relationLoading = false;
});
},
onLineClick(lineObject, $event) {
console.log("onLineClick:", lineObject);
},
},
};
</script>
<style lang="scss">
</style>
我:“哇,领导,结构树我都写出来了,太棒了。”
领导:“你实现的什么呀,给你那么好看的原型图,你就造出来这个,虽然做不了一模一样,但你看看人家插件demo的那两个例子,也挺好看啊”。
我:“领导呀,你听我狡辩,不对听我解释,其实咱就是按照第一个demo写的,他不就多了点配色,多了点连接线的文字么,至于第二个嘛,你看看他左侧的导航怎么解释的,一眼过去三个大字 ------- 手工设定坐标,然后再打开源码瞅一眼,这妥妥的老牛嘛,一个节点一个节点设置偏移量offset_x,offset_y。 但是咱们的子节点层级都不确定,能这么做出来就最好了”
插件应该是一个大佬个人在维护,为此我都联系了官网大佬的qq专门咨询,大佬都特别有耐心竟然真能回复问题,但是好像因为大佬精力有限,文档后续没有在维护,所以呢,跟领导沟通沟通箭头朝哪无所谓吧,要不就不要箭头了,不要箭头的属性还是好使的。link中添加属性(isReverse为反向箭头属性不生效,isHideArrow隐藏箭头好使)
links: [
{ from: "101", to: "0", "isHideArrow": true,"isReverse": true,},
],
我:“领导连接线股份比例不要了吧,不显示多简约呀”
当然更期待有一天插件大佬能完善优化点
之前功能不变,点击展开按钮,加载子节点单侧数据,点击节点本身,弹出dialog,将它作为根节点加载它的两侧数据。
<template>
<div class="legalperson">
<div style="height: calc(100vh - 50px)" v-loading="relationLoading">
<SeeksRelationGraph
ref="seeksRelationGraph"
:options="graphOptions"
:on-node-click="onNodeClick"
:on-line-click="onLineClick"
:on-node-expand="onNodeExpand"
/>
</div>
<el-dialog :title="title" :visible.sync="dialogapproval" width="70%">
<div style="height: calc(63vh)" v-loading="dialogLoading">
<SeeksRelationGraph
ref="other"
:options="othergraphOptions"
:on-node-click="otheronNodeClick"
:on-node-expand="otheronNodeExpand"
/>
</div>
<div slot="footer" class="dialog-footer">
<el-button size="medium" @click="dialogapproval = false"
>关闭</el-button
>
</div>
</el-dialog>
</div>
</template>
<script>
import SeeksRelationGraph from "relation-graph";
import { query_tree1 } from "@/api/affiliated";
export default {
name: "legalperson",
components: { SeeksRelationGraph },
data() {
return {
dialogapproval: false,
relationLoading: false,
dialogLoading: false,
currentCase: "双向树",
isShowCodePanel: false,
graphOptions: {
layouts: [
{
label: "中心",
layoutName: "tree",
layoutClassName: "seeks-layout-center",
defaultJunctionPoint: "border",
defaultNodeShape: 0,
defaultLineShape: 1,
// min_per_height: "100",
// centerOffset_x: -300,
centerOffset_y: 130,
min_per_width: 150,
min_per_height: 180,
},
],
defaultNodeShape: 1,
defaultExpandHolderPosition: "bottom", //节点展开关闭的按钮位置
defaultLineShape: 4,
defaultJunctionPoint: "tb", //
defaultNodeBorderWidth: 0.2, //节点边框粗细
// defaultLineColor: "rgba(0, 186, 189, 1)",
// defaultNodeBorderColor: "rgba(199, 199, 199, 1)",
// defaultNodeFontColor: "rgba(67, 67, 67, 1)",
// defaultNodeColor: "rgba(255, 255, 255, 1)",
// defaultLineColor: "#c7c7c7",
defaultLineColor: "rgba(0, 186, 189, 1)",
defaultNodeColor: "rgba(0, 206, 209, 1)",
defaultNodeWidth: "130", //节点宽度
defaultNodeHeight: "80", //节点高度
defaultFocusRootNode: false,
moveToCenterWhenResize: false,
},
//弹窗dialog图配置
othergraphOptions: {
layouts: [
{
label: "中心",
layoutName: "tree",
layoutClassName: "seeks-layout-center",
defaultJunctionPoint: "border",
defaultNodeShape: 0,
defaultLineShape: 1,
// min_per_height: "100",
centerOffset_x: 100,
centerOffset_y: 70,
min_per_width: 150,
min_per_height: 180,
},
],
defaultNodeShape: 1,
defaultExpandHolderPosition: "bottom", //节点展开关闭的按钮位置
defaultLineShape: 4,
defaultJunctionPoint: "tb", //
defaultNodeBorderWidth: 0.2, //节点边框粗细
// defaultLineColor: "rgba(0, 186, 189, 1)",
// defaultNodeBorderColor: "rgba(199, 199, 199, 1)",
// defaultNodeFontColor: "rgba(67, 67, 67, 1)",
// defaultNodeColor: "rgba(255, 255, 255, 1)",
// defaultLineColor: "#c7c7c7",
defaultLineColor: "rgba(0, 186, 189, 1)",
defaultNodeColor: "rgba(0, 206, 209, 1)",
defaultNodeWidth: "130", //节点宽度
defaultNodeHeight: "80", //节点高度
defaultFocusRootNode: false,
moveToCenterWhenResize: false,
},
title: "",
__graph_json_data: {},
otherData: {},
};
},
mounted() {
this.showSeeksGraph();
},
methods: {
//查询根节点
showSeeksGraph(query) {
this.__graph_json_data = {
rootId: "0",
nodes: [
{
id: "0",
offset_x: -59,
text: "中煤财产保险股份有限公司",
width: 250,
color: "#0084ff",
fontColor: "#ffffff",
childrenLoaded: true,
},
// { id: "102", text: "对外投资", color: '#83D942', expandHolderPosition: 'bottom', expanded: false, childrenLoaded: false, },
// { id: "101", text: "股东",},
],
links: [
// { from: "101", to: "0", color: "#6d9bfd", },
],
};
this.$refs.seeksRelationGraph.setJsonData(
this.__graph_json_data,
(seeksRGGraph) => {
this.shareholdersData();
}
);
},
//加载股东数据
shareholdersData() {
this.relationLoading = true;
let dictionary = {
parentcodeName: "中煤财产保险股份有限公司",
relatedtypedetail: "101",
};
query_tree1(dictionary)
.then((res) => {
this.__graph_json_data.nodes = res.data.nodes;
this.__graph_json_data.links = res.data.links;
// Promise.all(
// this.__graph_json_data.links.map((item) => {
// // item.color = '#c7c7c7';
// })
// ).then(() => {
// this.$refs.seeksRelationGraph.refresh() //刷新布局
this.$refs.seeksRelationGraph.appendJsonData(
this.__graph_json_data,
(seeksRGGraph) => {
this.foreignData();
}
);
// });
})
.catch(() => {
this.relationLoading = false;
});
},
//加载对外投资数据
foreignData() {
let dictionary = {
parentcodeName: "中煤财产保险股份有限公司",
relatedtypedetail: "102",
};
query_tree1(dictionary)
.then((res) => {
this.__graph_json_data.nodes = res.data.nodes;
this.__graph_json_data.links = res.data.links;
// Promise.all(
// this.__graph_json_data.links.map((item) => {
// // item.color = '#c7c7c7';
// })
// ).then(() => {
// this.$refs.seeksRelationGraph.refresh()
this.$refs.seeksRelationGraph.appendJsonData(
this.__graph_json_data,
(seeksRGGraph) => {
this.relationLoading = false;
}
);
// });
})
.catch(() => {
this.relationLoading = false;
});
},
//点击节点触发
onNodeClick(node, $event) {
// this.additionalData(node);
this.dialogapproval = true;
this.$nextTick(() => {
this.other(node);
});
},
//点击展开节点按钮触发
onNodeExpand(node, e) {
if (node.id !== "0") {
this.additionalData(node);
}
},
//像图谱中追加数据
additionalData(info) {
console.log(info);
let dictionary = {
parentcodeName: info.text,
parentcode: info.id,
};
if (info.expandHolderPosition == "top") {
dictionary.relatedtypedetail = "101";
} else if (info.expandHolderPosition == "bottom") {
dictionary.relatedtypedetail = "102";
}
//判断是否已经动态加载数据了
if (info.data.childrenLoaded) {
console.log("这个节点的子节点已经加载过了");
// this.$refs.seeksRelationGraph.refresh()
return;
}
info.data.expanded = true;
info.data.childrenLoaded = true;
this.relationLoading = true;
query_tree1(dictionary)
.then((res) => {
this.__graph_json_data.nodes = res.data.nodes;
this.__graph_json_data.links = res.data.links;
// this.$refs.seeksRelationGraph.refresh()
this.$refs.seeksRelationGraph.appendJsonData(
this.__graph_json_data,
(seeksRGGraph) => {
this.relationLoading = false;
}
);
})
.catch(() => {
this.relationLoading = false;
});
},
onLineClick(lineObject, $event) {
console.log("onLineClick:", lineObject);
},
//--------------------------------------------------弹框展示企业架构图
//点击企业信息弹框,展示各个企业的架构图
other(info) {
console.log(info);
this.title = info.text;
this.otherData = {
rootId: info.id,
nodes: [
{
offset_x: -59,
id: info.id,
text: info.text,
width: 250,
color: "#0084ff",
fontColor: "#ffffff",
childrenLoaded: true,
},
],
links: [],
};
this.$nextTick(() => {
// dom元素更新后执行,因此这里能正确打印更改之后的值
this.$refs.other.setJsonData(this.otherData, (seeksRGGraph) => {
this.othershareholdersData(info);
});
});
},
//加载股东数据
othershareholdersData(info) {
this.dialogLoading = true;
let dictionary = {
parentcodeName: info.text,
relatedtypedetail: "101",
};
query_tree1(dictionary)
.then((res) => {
this.otherData.nodes = res.data.nodes;
this.otherData.links = res.data.links;
this.$refs.other.appendJsonData(this.otherData, (seeksRGGraph) => {
this.otherforeignData(info);
});
})
.catch(() => {
this.dialogLoading = false;
});
},
//加载对外投资数据
otherforeignData(info) {
let dictionary = {
parentcodeName: info.text,
relatedtypedetail: "102",
};
query_tree1(dictionary)
.then((res) => {
this.otherData.nodes = res.data.nodes;
this.otherData.links = res.data.links;
this.$refs.other.appendJsonData(this.otherData, (seeksRGGraph) => {
this.dialogLoading = false;
});
})
.catch(() => {
this.dialogLoading = false;
});
},
//点击节点触发
otheronNodeClick(node, $event) {
this.other(node);
},
//点击展开节点按钮触发
otheronNodeExpand(node, e) {
if (node.id !== "0") {
this.otheradditionalData(node);
}
},
//向图谱中追加数据
otheradditionalData(info) {
console.log(info);
let dictionary = {
parentcodeName: info.text,
parentcode: info.id,
};
if (info.expandHolderPosition == "top") {
dictionary.relatedtypedetail = "101";
} else if (info.expandHolderPosition == "bottom") {
dictionary.relatedtypedetail = "102";
}
//判断是否已经动态加载数据了
if (info.data.childrenLoaded) {
console.log("这个节点的子节点已经加载过了");
// this.$refs.other.refresh()
return;
}
info.data.expanded = true;
info.data.childrenLoaded = true;
this.dialogLoading = true;
query_tree1(dictionary)
.then((res) => {
this.otherData.nodes = res.data.nodes;
this.otherData.links = res.data.links;
this.$refs.other.appendJsonData(this.otherData, (seeksRGGraph) => {
this.dialogLoading = false;
});
})
.catch(() => {
this.dialogLoading = false;
});
},
},
};
</script>
<style lang="scss">
</style>
留言:
1:官网demo很多,github如果打不开可以复制地址发送到微信,直接微信点击地址可以查看源码
2:虽然代码很多,简单一句话总结就是方法setJsonData加载初始数据,方法appendJsonData追加数据,
3:经过查阅资料最终选定的符合公司要求的方案,欢迎大家有更好的实现方案沟通交流
文章地址:https://blog.csdn.net/wangjiecsdn/article/details/130488425?spm=1001.2014.3001.5501