赞
踩
在做组织关系图时,经常会遇到关系图的实现要求,就是要将人与人或者组织与组织或者人与组织之间的关系进行一一展示。已知的就是节点和关系。
最近在写后台管理系统时,遇到一个需求,就是要实现关系图:
如下图所示:
在前年写天眼查功能时,我也遇到过这种需求,不过当时不知道可以用插件来实现,因此功能并未完全开发:
直接上代码:
npm i relation-graph
yarn add relation-graph
<template>
<div>
<div style="height:calc(100vh - 50px);">
<RelationGraph ref="seeksRelationGraph" :options="graphOptions" :on-node-click="onNodeClick" :on-line-click="onLineClick" />
</div>
</div>
</template>
<script> import RelationGraph from 'relation-graph';//引入插件 export default { name: 'Demo', components: { RelationGraph },//注册插件 data() { return { //设置插件的参数 graphOptions: { allowSwitchLineShape: true, allowSwitchJunctionPoint: true, defaultJunctionPoint: 'border' // 这里可以参考"Graph 图谱"中的参数进行设置:http://relation-graph.com/#/docs/graph } } }, mounted() { this.showSeeksGraph() }, methods: { showSeeksGraph() { //需要指定 节点参数和连接线的参数 var __graph_json_data = { rootId: 'a', nodes: [ // node配置选项:http://relation-graph.com/#/docs/node // node支持通过插槽slot完全自定义,示例:http://relation-graph.com/#/demo/adv-slot { 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: [ // link配置选项:http://relation-graph.com/#/docs/link { 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' } ] } this.$refs.seeksRelationGraph.setJsonData(__graph_json_data, (seeksRGGraph) => { // Called when the relation-graph is completed }) }, onNodeClick(nodeObject, $event) { console.log('onNodeClick:', nodeObject) }, onLineClick(linkObject, $event) { console.log('onLineClick:', linkObject) } } } </script>
<template> <a-spin :spinning="loading"> <div style="height: calc(100vh - 130px); border: 1px solid #ebebeb" ref="myPage" > <RelationGraph ref="seeksRelationGraph" :options="graphOptions" :on-node-click="onNodeClick" :on-line-click="onLineClick" > <div slot="node" slot-scope="{ node }" style="height: 100%" @mouseover="showNodeTips(node, $event)" @mouseout="hideNodeTips(node, $event)" > <div style=" border-radius: 50%; cursor: pointer; word-break: break-all; display: flex; justify-content: center; align-items: center; color: #fff; height: 100%; font-size: 12px; overflow: hidden; " > {{ node.text && node.text .replace('https://www.', '') .replace('http://www.', '') .replace('.com', '') .replace('.html', '') .split('/').length > 1 ? node.text .replace('https://www.', '') .replace('http://www.', '') .replace('.com', '') .replace('.html', '') .split('/')[ node.text .replace('https://www.', '') .replace('http://www.', '') .replace('.com', '') .replace('.html', '') .split('/').length - 1 ]||node.text .replace('https://www.', '') .replace('http://www.', '') .replace('.com', '') .replace('.html', '') .split('/')[ node.text .replace('https://www.', '') .replace('http://www.', '') .replace('.com', '') .replace('.html', '') .split('/').length - 2 ] : node.text }} </div> </div> <!-- <div slot="bottomPanel" style=" border-top: #efefef solid 1px; height: 60px; line-height: 60px; text-align: center; font-size: 18px; background-color: #ffffff; " > 这里是底部插槽 slot="bottomPanel",可以自定义这里的内容 </div> --> </RelationGraph> </div> <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 style=" line-height: 25px; padding-left: 10px; color: #888888; font-size: 12px; " > 节点名称:{{ currentNode.text }} </div> </div> </a-spin> </template>
<script> import RelationGraph from 'relation-graph'; import { getRelationship } from '@/services/statistics';//我这边的接口地址,需要改成你自己的 export default { components: { RelationGraph }, data() { return { loading: false, data: [], activeKey: '', src: '', isShowCodePanel: false, isShowNodeTipsPanel: false, nodeMenuPanelPosition: { x: 0, y: 0 }, currentNode: {}, graphOptions: { allowSwitchLineShape: true, allowSwitchJunctionPoint: true, layouts: [ { label: '中心', layoutName: 'force', //布局方式(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: 0, //默认的节点形状,0:圆形;1:矩形 defaultExpandHolderPosition: 'bottom', //节点展开关闭的按钮位置 defaultLineShape: 1, //默认的线条样式(1:直线/2:样式2/3:样式3/4:折线/5:样式5/6:样式6) defaultJunctionPoint: 'tb', //默认的连线与节点接触的方式(border:边缘/ltrb:上下左右/tb:上下/lr:左右)当布局为树状布局时应使用tb或者lr,这样才会好看 defaultNodeBorderWidth: 0.2, //节点边框粗细 defaultcolor: 'rgba(0, 186, 189, 1)', //默认的线条颜色 defaultNodeColor: 'rgba(0, 206, 209, 1)', //默认的节点背景颜色 defaultNodeWidth: '80', //节点宽度 defaultNodeHeight: '80', //节点高度 defaultFocusRootNode: false, //默认为根节点添加一个被选中的样式 moveToCenterWhenResize: true, //当图谱的大小发生变化时,是否重新让图谱的内容看起来居中 // 这里可以参考"Graph 图谱"中的参数进行设置 }, }; }, activated() { this.showSeeksGraph(); }, 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; }, callback(val) { this.activeKey = val; this.showSeeksGraph(); }, //渲染节点和连接线 showSeeksGraph() { getRelationship().then((res) => { let nodes = res.node_list || []; let links = res.edge_list || []; var __graph_json_data = { rootId: '0', nodes: nodes, links: links, }; // 以上数据中的node和link可以参考"Node节点"和"Link关系"中的参数进行配置 this.$refs.seeksRelationGraph.setJsonData( __graph_json_data, (graphInstance) => { // Called when the relation-graph is completed setTimeout(() => { graphInstance.stopAutoLayout(); }, 1000); } ); }); }, //点击节点触发的函数 onNodeClick(nodeObject, $event) { const allLinks = this.$refs.seeksRelationGraph.getLinks(); allLinks.forEach((link) => { // 还原所有样式 link.relations.forEach((line) => { if (line.data.orignColor) { line.color = line.data.orignColor; } if (line.data.orignFontColor) { line.fontColor = line.data.orignColor; } if (line.data.orignLineWidth) { line.lineWidth = line.data.orignLineWidth; } }); }); // 让与{nodeObject}相关的所有连线高亮 allLinks .filter( (link) => link.fromNode === nodeObject || link.toNode === nodeObject ) .forEach((link) => { link.relations.forEach((line) => { line.data.orignColor = line.color; line.data.orignFontColor = line.fontColor || line.color; line.data.orignLineWidth = line.lineWidth || 1; line.color = '#ff0000'; line.fontColor = '#ff0000'; line.lineWidth = 3; }); }); // 有时候更改一些属性后,并不能马上同步到视图,这需要以下方法让视图强制根据数据同步到最新 this.$refs.seeksRelationGraph.getInstance().dataUpdated(); }, //店家连接线触发的函数 onLineClick(lineObject, $event) { console.log('onLineClick:', lineObject); // this.$notify({ // title: '点击连线:', // type: 'success', // message: '点击了线:' + linkObject.fromNode.text + ' to ' + linkObject.toNode.text // }); }, }, }; </script>
<style lang="less" scoped> .c-my-node2 { border: none; background-position: center center; background-size: 100%; height: 74px; width: 74px; border-radius: 40px; } .c-node-name2 { width: 160px; margin-left: -40px; text-align: center; margin-top: 85px; position: absolute; } .c-node-menu-item { line-height: 30px; padding-left: 10px; cursor: pointer; color: #444444; font-size: 14px; border-top: #efefef solid 1px; } .c-node-menu-item:hover { background-color: rgba(66, 187, 66, 0.2); } </style>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。