js地址:
组件:
<template>
<div class="graph">
<div class="graph-panel" id="graph-panel" ref="graphPanelRef"></div>
</div>
</template>
<script>
import { VisGraph } from "@/assets/GraphVis/visgraph.min.js";
import LayoutFactory from "@/assets/GraphVis/visgraph-layout.min.js";
import screenfull from "screenfull";
export default {
name: "RelationshipGraph",
components: {
VisGraph,
LayoutFactory,
},
props: {
// 图谱数据
graphData: {
type: Object,
default: null,
},
// 中心点id
centerNodeId: {
type: [String, Number],
default: null,
},
},
data() {
return {
visgraph: null,
centerNode: {},
currentNode: [],
canvasConfig: {
node: {
label: {
show: true,
color: "250,250,250",
font: "normal 14px Microsoft YaHei",
textPosition: "Middle_Center",
borderWidth: 0,
wrapText: true,
},
shape: "circle",
width: 60,
height: 60,
color: "62,160,250",
borderColor: "62,160,250",
borderWidth: 0,
borderRadius: 0,
lineDash: [0],
alpha: 1,
selected: {
borderColor: "136,198,255",
borderAlpha: 1,
borderWidth: 3,
showShadow: true,
shadowColor: "136,198,255",
},
onClick: (event, node) => {
this.$emit("canvasClick", { node });
},
ondblClick: (event, node) => {
this.$emit("canvasDbClick", { node });
},
onMouseOver: (event, node) => {
this.$emit("canvasMouseOver", { node });
},
onMouseOut: (event, node) => {
this.$emit("canvasMouseOut", { node });
},
},
link: {
label: {
show: true,
color: "100,100,200",
font: "normal 10px Arial",
},
lineType: "direct",
colorType: "defined",
color: "200,200,200",
alpha: 1,
lineWidth: 1,
lineDash: [0],
showArrow: true,
selected: {
color: "20,250,50",
alpha: 1,
lineWidth: 4,
showShadow: true,
shadowColor: "50,250,50",
},
},
highLightNeiber: true,
wheelZoom: 0.8,
noElementClick: (event, _graphvis) => {
// 点击画布其他位置,弹框隐藏
},
},
};
},
methods: {
/**
* @description: 处理图谱数据
* @return {*}
*/
handleData(data) {
const obj = {
nodes: [],
links: [],
};
data.nodes.forEach((item) => {
const nodeObj = {
id: item.id,
label: item.label,
properties: item,
size: 60,
};
if (item.id === this.centerNodeId) {
nodeObj.color = "38,186,191";
nodeObj.selectedBorderColor = "131,218,228";
nodeObj.shadowColor = "131,218,228";
nodeObj.size = 80;
nodeObj.x = 250;
nodeObj.y = 250;
this.centerNode = nodeObj;
this.currentNode.push(item.id);
} else {
obj.nodes.push(nodeObj);
}
});
if (!this.centerNodeId) {
this.centerNode = {
...obj.nodes[0],
color: "38,186,191",
selectedBorderColor: "38,186,191",
shadowColor: "38,186,191",
size: 80,
x: 250,
y: 250,
};
this.currentNode = obj.nodes[0].id;
}
obj.links = data.edges.map((item) => {
const linkObj = {
id: item.id,
target: item.to,
source: item.from,
label: item.label,
properties: item,
strokeColor: this.getRandomColor(),
};
switch (item.type) {
case 11:
linkObj.color = "40,194,199";
linkObj.selectedColor = "40,194,199";
linkObj.shadowColor = "40,194,199";
break;
case 12:
linkObj.color = "250,108,100";
linkObj.selectedColor = "250,108,100";
linkObj.shadowColor = "250,108,100";
break;
case 13:
linkObj.color = "0,132,255";
linkObj.selectedColor = "0,132,255";
linkObj.shadowColor = "0,132,255";
break;
case 15:
linkObj.color = "250,108,100";
linkObj.selectedColor = "250,108,100";
linkObj.shadowColor = "250,108,100";
break;
}
return linkObj;
});
this.buildData(obj);
},
/**
* @description: 搭建实例
* @param {*} gxData
* @return {*}
*/
buildData(gxData) {
const visgraphRef = this.$refs["graphPanelRef"];
this.visgraph = new VisGraph(visgraphRef, this.canvasConfig);
const nodeCount = gxData.nodes.length;
const xyArr = this.getXY(this.centerNode, nodeCount, nodeCount * 20);
gxData.nodes.forEach((n, i) => {
n.x = xyArr[i].x;
n.y = xyArr[i].y;
n.size = 60;
});
gxData.nodes.push(this.centerNode);
this.visgraph.drawData(gxData);
this.visgraph.setZoom();
},
/**
* 遍布选中节点周围的点坐标
* @date 2023-07-14
* @param {中心节点} centerNode
* @param {节点数量} nodeCount
* @param {距离中心点距离} raduis
* @returns {Array}
*/
getXY(centerNode, nodeCount, raduis) {
const aop = 360.0 / nodeCount;
const arr = [];
for (let i = 0; i < nodeCount; i++) {
let r1 = raduis;
if (nodeCount > 10) {
r1 = i % 2 === 0 ? raduis + 35 : raduis - 35;
} else if (nodeCount < 5) {
r1 = 200;
}
const ao = i * aop;
const o1 = {};
o1.x = centerNode.x + r1 * Math.cos((ao * Math.PI) / 180);
o1.y = centerNode.y + r1 * Math.sin((ao * Math.PI) / 180);
arr[i] = o1;
}
return arr;
},
/**
* @description: 以id寻找节点移动到中心
* @param {*} id
* @return {*}
*/
moveCenterThisNode(id) {
const node = this.visgraph.findNodeById(id);
this.visgraph.moveNodeToCenter(node);
},
/**
* @description: 动态添加节点
* @param {*} graphData
* @param {*} node
* @return {*}
*/
activeAddNodeLinks(graphData, node) {
this.visgraph.restoreHightLight();
this.currentNode.push(node.id);
this.visgraph.activeAddNodeLinks(graphData.nodes, graphData.links);
this.moveCenterThisNode(node.id);
},
/**
* 随机获取颜色
* @date 2021-07-20
* @returns {String} 样式
*/
getRandomColor() {
const r = Math.floor(Math.random() * 256);
const g = Math.floor(Math.random() * 256);
const b = Math.floor(Math.random() * 256);
return "rgb(" + r + "," + g + "," + b + ")";
},
/**
* 颜色选择框变化赋值
* @date 2021-07-26
* @param {String} val
* @param {String} kay
*/
colorChange(val, key) {
this.nodeConfigForm[key] = this.hexToRgba(val);
},
/**
* 16进制色值转rgb
* @date 2021-07-26
* @param {String} hex
* @returns {String}
*/
hexToRgba(hex) {
return (
"rgb(" +
parseInt("0x" + hex.slice(1, 3)) +
"," +
parseInt("0x" + hex.slice(3, 5)) +
"," +
parseInt("0x" + hex.slice(5, 7)) +
")"
);
},
/**
* rgb色值转16进制
* @date 2021-07-26
* @param {String} color
* @returns {String}
*/
rgbToHex(color) {
const rgb = color.split(",");
const r = parseInt(rgb[0].split("(")[1]);
const g = parseInt(rgb[1]);
const b = parseInt(rgb[2].split(")")[0]);
const hex =
"#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
return hex;
},
/**
* 保存图片
* @date 2021-07-23
*/
saveImage() {
this.visgraph.saveImage();
},
/**
* 导出json
* @date 2021-07-30
*/
exportJson() {
this.visgraph.exportJsonFile();
},
/**
* 打开缩略图
* @date 2021-07-23
*/
showOverView() {
this.showOverViewFlag = !this.showOverViewFlag;
this.visgraph.showOverView(this.showOverView);
},
/**
* 缩小操作
* @date 2021-07-23
*/
setZoomIn() {
this.visgraph.setZoom("zoomIn");
},
/**
* 放大操作
* @date 2021-07-23
*/
setZoomOut() {
this.visgraph.setZoom("zoomOut");
},
/**
* 顺时针旋转
* @date 2021-07-23
*/
clockwiseRotate() {
this.visgraph.rotateGraph(-10);
},
/**
* 逆时针旋转
* @date 2021-07-23
*/
counterclockwiseRotate() {
this.visgraph.rotateGraph(10);
},
/**
* 设置鼠标模式
* @date 2021-07-23
* @param {String} type drag:拖动模式 select:框选模式 normal:正常模式
*/
setMouseModel(type) {
this.visgraph.setMouseModel(type);
},
/**
* 全屏显示
* @date 2021-07-23
*/
fullScreen() {
screenfull.request(this.$refs['graphPanelRef']);
},
},
};
</script>
<style lang="scss" scoped>
.graph {
width: 100%;
height: 100%;
position: relative;
&-panel {
width: 100%;
height: 100%;
}
}
</style>
使用组件:
<template>
<div id="container">
<relationship-graph
ref="relationshipGraph"
:graphData="graphData"
:centerNodeId="20"
@canvasDbClick="canvasDbClick"
/>
</div>
</template>
<script>
import RelationshipGraph from "@/components/relationship-graph";
export default {
name: "Demo",
components: {
RelationshipGraph,
},
data() {
return {
graphData: {
edges: [
{
from: 20,
id: 24,
label: "股东",
to: 57,
},
{
from: 20,
id: 25,
label: "股东",
to: 58,
},
{
from: 20,
id: 35,
label: "100.000000",
to: 81,
},
{
from: 20,
id: 27,
label: "股东",
to: 59,
},
{
from: 20,
id: 30,
label: "高管",
to: 21,
},
{
from: 20,
id: 36,
label: "100.000000",
to: 82,
},
{
from: 20,
id: 37,
label: "60.000000",
to: 83,
type: 15,
},
{
from: 20,
id: 32,
label: "高管",
to: 62,
},
{
from: 20,
id: 33,
label: "高管",
to: 63,
},
{
from: 20,
id: 31,
label: "高管",
to: 58,
},
{
from: 20,
id: 5,
label: "法人",
to: 21,
},
{
from: 20,
id: 26,
label: "股东",
to: 21,
},
{
from: 20,
id: 28,
label: "股东",
to: 60,
},
],
nodes: [
{
id: 83,
label: "子公司1",
},
{
id: 81,
label: "子公司2",
},
{
id: 21,
label: "法人1",
},
{
id: 57,
label: "合伙1",
},
{
id: 59,
label: "法人2",
},
{
id: 62,
label: "法人3",
},
{
id: 20,
label: "总公司",
},
{
id: 63,
label: "法人4",
},
{
id: 82,
label: "子公司3",
},
{
id: 58,
label: "法人5",
},
{
id: 60,
label: "法人6",
},
],
},
};
},
mounted() {
this.$refs["relationshipGraph"].handleData(this.graphData);
},
methods: {
canvasDbClick({ node }) {
console.log(node);
const findNodeNum = Math.round(Math.random() * 50);
const increamData = this.buildIncreamData(node, findNodeNum);
this.$refs["relationshipGraph"].activeAddNodeLinks(increamData, node)
},
buildIncreamData(centerNode, nodeNum) {
const gxData = {
nodes: [],
links: [],
};
const count = nodeNum;
const nodeIdPrefix = "node_" + Math.round(Math.random() * 1000) + "_";
for (let i = 0; i < count; i++) {
gxData.nodes.push({
id: nodeIdPrefix + i,
label: "子节点+" + i,
size: 60,
color: this.getRandomColor(),
});
gxData.links.push({
source: centerNode.id,
target: nodeIdPrefix + i,
label: "关系" + i,
});
}
return gxData;
},
getRandomColor() {
const r = Math.floor(Math.random() * 256);
const g = Math.floor(Math.random() * 256);
const b = Math.floor(Math.random() * 256);
return "rgb(" + r + "," + g + "," + b + ")";
},
},
};
</script>
<style lang="scss" scoped>
#container {
width: 100%;
height: 100%;
}
</style>
最终效果: