关系图插件relation-graph的使用(适用vue2、vue3、react)
【代码】关系图插件relation-graph的使用(适用vue2、vue3、react)
·
1、安装教程
npm install --save relation-graph
如果下载引用时报min.js引用错误则需要在vue.config.js进行配置
configureWebpack: {
module: {
rules: [{
test: /\.mjs$/,
include: /node_modules/,
type: 'javascript/auto'
}]
}
},
2、使用案列
<template>
<div>
<div style="height:calc(100vh - 60px);">
<RelationGraph ref="graphRef" :options="graphOptions" :on-node-click="onNodeClick" :on-line-click="onLineClick" />
</div>
</div>
</template>
<script>
// relation-graph也支持在main.js文件中使用Vue.use(RelationGraph);这样,你就不需要下面这一行代码来引入了。
import RelationGraph from 'relation-graph'
export default {
name: 'Demo',
components: { RelationGraph },
data() {
return {
graphOptions: {
defaultJunctionPoint: 'border'
// 这里可以参考"Graph 图谱"中的参数进行设置 https://www.relation-graph.com/#/docs/graph
}
}
},
mounted() {
this.showGraph()
},
methods: {
showGraph() {
const jsonData = {
rootId: 'a',
nodes: [
{ id: 'a', text: 'A', borderColor: 'yellow' },
{ id: 'b', text: 'B', color: '#43a2f1', fontColor: 'yellow' },
{ id: 'c', text: 'C', nodeShape: 1, width: 80, height: 60 },
{ id: 'e', text: 'E', nodeShape: 0, width: 150, height: 150 }
],
lines: [
{ from: 'a', to: 'b', text: '关系1', color: '#43a2f1' },
{ from: 'a', to: 'c', text: '关系2' },
{ from: 'a', to: 'e', text: '关系3' },
{ from: 'b', to: 'e', color: '#67C23A' }
]
}
// 以上数据中的node和link可以参考"Node节点"和"Link关系"中的参数进行配置
this.$refs.graphRef.setJsonData(jsonData, (graphInstance) => {
// Called when the relation-graph is completed
})
},
onNodeClick(nodeObject, $event) {
console.log('onNodeClick:', nodeObject)
},
onLineClick(lineObject, $event) {
console.log('onLineClick:', lineObject)
}
}
}
</script>
3、个别特殊算法案列(非正规层级树形,一个节点有多个父亲,线条箭头向下一种方向延申、可点击重新渲染相关关系如有节点及线条,变更颜色等)
<template>
<div>
<div
v-if="nodeList?.length > 0"
ref="myPage"
class="my-graph"
style="height: calc(80vh)"
>
<RelationGraph
ref="graphRef"
:options="graphOptions"
:on-node-click="onNodeClick"
:on-line-click="onLineClick"
>
<template #node="{ node }">
<div
@mouseover="showNodeTips(node, $event)"
@mouseout="hideNodeTips(node, $event)"
>
<div class="c-my-rg-node">
<i style="font-size: 30px" :class="node.data.myicon" />
</div>
<!-- width: 160px; -->
<div
v-show="value1"
style="
font-size: 16px;
position: absolute;
height: 25px;
line-height: 25px;
margin-top: 20px;
margin-left: -48px;
text-align: left;
"
>
<span style="color: #212026">{{ node.data.text }}</span>
<br />
<span style="color: #71747a">{{ node.data.tableDesc }}</span>
</div>
</div>
</template>
</RelationGraph>
<div
v-if="isShowNodeTipsPanel"
:style="{
left: nodeMenuPanelPosition.x + 'px',
top: nodeMenuPanelPosition.y + 'px',
}"
style="
z-index: 999;
padding: 10px;
background-color: #ffffff;
border: #eeeeee solid 1px;
box-shadow: 0px 0px 8px #cccccc;
position: absolute;
"
>
<div class="c-node-menu-item">
<span
style="
display: inline-block;
border-left: 2px solid #3c91ff;
padding-left: 4px;
color: #212026;
"
>{{ currentNode?.data?.text }}</span
>
</div>
<div class="c-node-menu-item">
<span
style="display: inline-block; padding-left: 4px; color: #71747a"
>{{ currentNode?.data?.tableDesc }}</span
>
</div>
</div>
</div>
<div
v-else
style="
background: #fff;
font-size: 22px;
color: #ccc;
text-align: center;
height: calc(70vh);
line-height: calc(70vh);
"
>
暂无数据
</div>
</div>
</template>
<script>
// 如果您没有在main.js文件中使用Vue.use(RelationGraph); 就需要使用下面这一行代码来引入relation-graph
import RelationGraph from "relation-graph";
import dagre from "dagre";
export default {
name: "TreeRelation",
components: { RelationGraph },
data() {
return {
edgeGroupSign: null,
currentNode: {},
linesList: [],
nodeList: [],
isShowNodeTipsPanel: false,
isShowCodePanel: false,
nodeMenuPanelPosition: { x: 0, y: 0 },
graphOptions: {
disableLineClickEffect: true,
// moveToCenterWhenRefresh: true,
// debug: false,
allowSwitchLineShape: true,
allowSwitchJunctionPoint: true,
// allowShowDownloadButton: true,
useAnimationWhenRefresh: true,
defaultLineWidth: 2,
defaultLineColor: "transparent",
lineUseTextPath: true,
layout: {
layoutName: "fixed",
layoutDirection: "v",
},
defaultNodeShape: 1,
defaultLineShape: 1,
defaultJunctionPoint: "tb",
defaultPloyLineRadius: 10,
defaultNodeBorderWidth: 0,
defaultPolyLineRadius: 20,
defaultNodeWidth: 54,
defaultNodeHeight: 54,
toolBarDirection: "h",
toolBarPositionH: "left",
toolBarPositionV: "top",
// defaultNodeColor: "rgba(0, 206, 209, 1)",
},
};
},
props: {
relationshipData: {
type: Object,
default: () => {},
},
singFlag: {
type: Boolean,
default: false,
},
value1: {
type: Boolean,
default: true,
},
},
watch: {
relationshipData: {
immediate: true,
deep: true,
handler(val) {
if (val?.tableList?.length > 0) {
this.nodeList = val?.tableList.map((item) => {
return {
id: String(item?.id),
color: item?.id == val?.node?.id ? "#3FD3A1" : "#3C91FF",
width: 54,
height: 54,
borderColor: "transparent",
borderWidth: 0,
data: {
myicon: "el-icon-notebook-2",
text: item?.tableName,
tableDesc: item?.tableDesc,
},
};
});
this.linesList = val?.edgeList.map((item1) => {
return {
from: String(item1?.fromTableId),
to: String(item1?.toTableId),
color: this.singFlag ? "#E5E5E5" : "#3C91FF",
lineWidth: 2,
edgeGroup: item1?.edgeGroup,
};
});
this.$nextTick(() => {
this.showGraph();
});
} else {
this.nodeList = [];
this.linesList = [];
this.$nextTick(() => {
this.showGraph();
});
}
},
},
},
mounted() {
// this.showGraph();
},
methods: {
showNodeTips(nodeObject, $event) {
this.currentNode = nodeObject;
const _base_position = this.$refs.myPage.getBoundingClientRect();
this.isShowNodeTipsPanel = true;
this.nodeMenuPanelPosition.x = $event.clientX - _base_position.x + 10;
this.nodeMenuPanelPosition.y = $event.clientY - _base_position.y + 10;
},
hideNodeTips(nodeObject, $event) {
this.isShowNodeTipsPanel = false;
},
getAllNodeLine(data) {
// console.log("点击拿到的类别", data, "this.linesList", this.linesList);
if (data) {
let arrIds = [];
// this.linesList = this.linesList.filter((item) => item.id);
this.linesList.forEach((item) => {
if (item?.edgeGroup == data) {
arrIds.push(item.from, item.to);
item.color = "#3FD3A1";
} else {
item.color = this.singFlag ? "#E5E5E5" : "#3C91FF";
}
});
arrIds = [...new Set(arrIds)];
this.nodeList.forEach((item) => {
if (arrIds.includes(item?.id)) {
item.color = "#3FD3A1";
} else {
item.color = "#3C91FF";
}
});
this.showGraph();
}
},
async showGraph() {
let __graph_json_data = {};
__graph_json_data = {
rootId: "a",
nodes: this.nodeList,
lines: this.linesList,
};
// __graph_json_data?.nodes?.forEach((node) => {
// // 随机设置节点宽高
// // node.width = 40 + Math.floor(Math.random() * 200)
// // node.height = 40
// });
let lineIndex = 0;
__graph_json_data?.lines?.forEach((line) => {
// console.log("line", line);
line.data = {
// 自定义属性放在data中
points: [], // 准备一个属性,待会儿接收从dagre生成的线条点
edgeGroup: line?.edgeGroup, //存放类别
};
line.lineDirection = "v";
line.id = "L" + lineIndex++; // id 必须是字符串
// line.text = "";
});
const g = new dagre.graphlib.Graph();
g.setGraph({
// ranker: "longest-path",
ranker: "tight-tree",
nodesep: 30,
ranksep: 200,
// rankdir: 'TB'
// rankdir:指定节点排名的方向,可以是从上到下(TB)、从下到上(BT)、从左到右(LR)或从右到左(RL)。
// align:指定排名节点的对齐方式,可以是左上(UL)、右上(UR)、左下(DL)或右下(DR)。
// nodesep:指定布局中水平分隔节点的像素数。
// edgesep:指定布局中水平分隔边的像素数。
// ranksep:指定布局中每个排名之间的像素数。
// marginx:指定图形左右两侧用作边缘的像素数。
// marginy:指定图形上下两侧用作边缘的像素数。
// acyclicer:如果设置为greedy,则使用贪心启发式算法找到图的反馈弧集,即一组可以移除的边,使得图变为无环。
// ranker:指定用于为输入图中的每个节点分配排名的算法类型,可以是network-simplex、tight-tree或longest-path
});
// g.setDefaultEdgeLabel(function () {
// return {};
// });
const graphInstance = this.$refs.graphRef?.getInstance();
await graphInstance?.setJsonData(__graph_json_data);
graphInstance?.getNodes()?.forEach((node) => {
node.width = node?.el?.offsetWidth;
node.height = node?.el?.offsetHeight;
// console.log('offsetHeight', node);
g.setNode(node?.id, node);
});
graphInstance?.getLinks()?.forEach((link) => {
link.relations.forEach((line) => {
g.setEdge(link.fromNode.id, link.toNode.id, {
id: line.id, // 设置id,到会儿通过id找到jsonData中的line,设置line.data.points
// weight: 0 // 这是一个非常重要的参数,尝试设置为0或者1或者中间值查看效果
});
});
});
dagre.layout(g);
graphInstance?.getNodes()?.forEach((node) => {
// 由于dagre生成的坐标是指向节点中心的,所以要根据节点宽高做偏移
node.x = node.x - node.el.offsetWidth / 2 - 5;
node.y = node.y - node.el.offsetHeight / 2 - 5;
});
g.edges().forEach((e) => {
const edge = g.edge(e); // 读取dagre生成的连线信息,写入relation-graph的线条属性中
// console.log("22222222222222222", edge);
const line = this.getLineById(graphInstance, edge.id);
const link = this.getLinkByLineId(graphInstance, edge.id);
line.data.points = edge.points;
line.data.startPointOffset = {
// 为了支持节点移动时线条跟着动
x: edge.points[0].x - link.fromNode.x,
y: edge.points[0].y - link.fromNode.y,
};
line.data.endPointOffset = {
// 为了支持节点移动时线条跟着动
x: edge.points[edge.points.length - 1].x - link.toNode.x,
y: edge.points[edge.points.length - 1].y - link.toNode.y,
};
});
console.log(graphInstance, "graphInstance------");
await graphInstance?.refresh();
await graphInstance?.moveToCenter();
await graphInstance?.zoomToFit();
},
getLineById(graphInstance, lineId) {
for (const link of graphInstance.getLinks()) {
for (const line of link.relations) {
if (line.id === lineId) {
return line;
}
}
}
},
getLinkByLineId(graphInstance, lineId) {
for (const link of graphInstance.getLinks()) {
for (const line of link.relations) {
if (line.id === lineId) {
return link;
}
}
}
},
onNodeClick(nodeObject, $event) {
// console.log("onNodeClick:", nodeObject, "this.linesList", this.linesList);
this.isShowNodeTipsPanel = false;
this.edgeGroupSign = null;
const flag = this.linesList.some((item) => {
if (item?.to == nodeObject?.id || item?.from == nodeObject?.id) {
this.edgeGroupSign = item?.edgeGroup;
return true;
}
});
// console.log(`flag:`, flag, "this.edgeGroupSign", this.edgeGroupSign);
if (!flag) {
this.edgeGroupSign = null;
this.nodeList.forEach((item) => {
if (item?.id == nodeObject?.id) {
item.color = "#3FD3A1";
} else {
item.color = "#3C91FF";
}
});
this.linesList.forEach((item) => {
item.color = this.singFlag ? "#E5E5E5" : "#3C91FF";
});
this.showGraph();
} else {
this.getAllNodeLine(this.edgeGroupSign);
}
},
onLineClick(lineObject) {
this.isShowNodeTipsPanel = false;
this.getAllNodeLine(lineObject?.data?.edgeGroup);
},
},
};
</script>
<style lang="scss"></style>
<style lang="scss" scoped>
.c-my-panel {
width: 400px;
position: absolute;
left: 10px;
top: 10px;
border-radius: 10px;
z-index: 800;
background-color: #efefef;
border: #eeeeee solid 1px;
padding: 10px;
.c-option-name {
color: #666666;
font-size: 14px;
line-height: 40px;
padding-left: 10px;
padding-right: 10px;
}
.c-my-options {
text-align: center;
.c-my-option-item {
text-align: left;
color: #1da9f5;
cursor: pointer;
border-radius: 5px;
padding-left: 10px;
margin-top: 5px;
line-height: 25px;
&:hover {
background-color: rgba(29, 169, 245, 0.2);
}
}
}
}
.c-my-rg-node {
height: 54px;
border-radius: 50%;
cursor: pointer;
display: flex;
place-items: center;
justify-content: center;
color: #d8d8d8;
}
.c-node-menu-item {
height: 30px;
display: flex;
align-items: center;
padding-left: 10px;
font-size: 14px;
}
::v-deep .rel-node {
height: 54px !important;
width: 54px !important;
}
/* ::v-deep .rel-map-canvas {
margin-left: 50px !important;
margin-top: 30px !important;
} */
</style>
更多推荐
所有评论(0)