使用relation-graph实现企业关系图谱
需求:需要显示企业的关系图谱。(由于项目使用的Vue,所以感觉这个作者写的还不错)
1、安装relation-graph(http://relation-graph.com/):
npm install --save relation-graph
2、引入relation-graph:
import RelationGraph from 'relation-graph';
3、创建dom:
<template> <div> <div class="graph" ref="graph" style="height:calc(100vh - 50px);" @click="canvasClick"> <RelationGraph ref="seeksRelationGraph" :options="graphOptions" :on-node-click="onNodeClick" :on-line-click="onLineClick"> <div slot="node" class="node" slot-scope="{ node }" @mouseover="showNodeTips(node, $event)" @mouseout="hideNodeTips(node, $event)"> <div v-if="node.data.labels.includes('Company')" class="company-node"> {{ node.text }} </div> <div v-else class="human-node">{{ node.text }}</div> </div> </RelationGraph> </div> <div v-if="isShowNodeTipsPanel" :style="{ left: nodeMenuPanelPosition.x + 'px', top: nodeMenuPanelPosition.y + 'px' }" class="node-dialog"> <div>{{ currentNode.text }}</div> </div> </div> </template>
4、js配置(数据来源于企查查的小米关系图谱数据):
<script> import RelationGraph from 'relation-graph'; import { xiaomi } from './xiaomi'; export default { name: 'RelationGraphIndex', components: { RelationGraph }, data() { return { // 图谱配置 graphOptions: { backgrounImage: require('@/assets/images/bg.webp'), // 水印 backgrounImageNoRepeat: true, // 只在右下角显示水印,不重复显示水印 allowShowMiniToolBar: true, // 是否显示工具栏 allowShowMiniView: true, // 是否显示缩略图 allowShowMiniNameFilter: false, // 是否显示搜索框 allowSwitchLineShape: true, // 是否在工具栏中显示切换线条形状的按钮 allowSwitchJunctionPoint: true, // 是否在工具栏中显示切换连接点位置的按钮 disableZoom: false, // 是否禁用图谱的缩放功能,这里特指通过鼠标滚轮进行缩放的功能,禁用后你依然可以通过图谱工具栏按钮进行缩放 disableDragNode: false, // 是否禁用图谱中节点的拖动 moveToCenterWhenResize: true, // 当图谱的大小发生变化时,是否重新让图谱的内容看起来居中 // defaultFocusRootNode: '', // 默认为根节点添加一个被选中的样式 allowShowZoomMenu: true, // 是否在右侧菜单栏显示放大缩小的按钮 isMoveByParentNode: true, // 是否在拖动节点后让子节点跟随 hideNodeContentByZoom: true, // 是否根据缩放比例隐藏节点内容 defaultNodeShape: 0, // 默认的节点形状,0:圆形;1:矩形 // defaultNodeColor: '', // 默认的节点背景颜色 // defaultNodeFontColor: '', // 默认的节点文字颜色 // defaultNodeBorderWidth: '2px', // 默认的节点边框粗细(像素) // defaultNodeWidth: '40px', // 默认的节点宽度 // defaultNodeHeight: '40px', // 默认的节点高度 defaultJunctionPoint: 'border', // 默认的连线与节点接触的方式(border:边缘 / ltrb:上下左右 / tb:上下 / lr:左右) // defaultExpandHolderPosition: 'right', // 默认的节点展开/关闭按钮位置(left/top/right/bottom) // defaultLineColor: '', // 默认的线条颜色 // defaultLineWidth: '', // 默认线条粗细(px) // defaultLineShape: '', // 默认的线条样式(1:直线/2:样式2/3:样式3/4:折线/5:样式5/6:样式6) // defaultLineMarker: '', // 默认的线条箭头样式 // defaultShowLineLabel: '', // 默认是否显示连线文字 // 布局方式 layouts: [ { layoutLabel: '自动布局', // 布局描述 layoutName: 'force', // 布局方式(tree树状布局/center中心布局/force自动布局) layoutClassName: 'seeks-layout-force' // 当使用这个布局时,会将此样式添加到图谱上 } ] }, // 选中点 currentNode: {}, // 显示提示弹框 isShowNodeTipsPanel: false, // 节点位置 nodeMenuPanelPosition: { x: 0, y: 0 } }; }, mounted() { this.showSeeksGraph(); }, methods: { /** * 加载图谱 * @date 2022-10-21 * @returns {any} */ showSeeksGraph() { let { nodes, relationships } = xiaomi; nodes = nodes.map((item) => { item.text = item.properties.name; item.data = { ...item }; if (item.labels.includes('Company')) (item.borderColor = '#4ea2f0'), (item.color = '#4ea2f0'), (item.styleClass = 'company-node'); if (item.labels.includes('Human')) (item.borderColor = '#ff6060'), (item.color = '#ff6060'), (item.width = 75), (item.height = 75); if (item.text === '小米科技有限责任公司') (item.borderColor = '#ffce7f'), (item.color = '#ff9e00'); return item; }); relationships = relationships.map((item) => { item.from = item.startNode; item.to = item.endNode; item.text = item.properties.role; return item; }); let centerIds = nodes.find((item) => item.text === '小米科技有限责任公司'); const obj = { rootId: centerIds.id, nodes, links: relationships }; // 以上数据中的node和link可以参考"Node节点"和"Link关系"中的参数进行配置 this.$refs.seeksRelationGraph.setJsonData(obj, (seeksRGGraph) => { // console.log(seeksRGGraph); }); }, /** * 节点点击 * @date 2022-10-21 * @param {any} nodeObject * @param {any} $event * @returns {any} */ onNodeClick(nodeObject, $event) { console.log('节点数据:', nodeObject); const _all_nodes = this.$refs['seeksRelationGraph'].getNodes(), _all_lines = this.$refs['seeksRelationGraph'].getLines(); const { lot } = nodeObject; // 子集高亮 if (lot.childs && lot.childs.length) { _all_nodes.forEach((thisNode) => { let _isHideThisNode = false; lot.childs.forEach((childNode) => { if (thisNode.id === childNode.id || thisNode.id === nodeObject.id) { _isHideThisNode = true; } if (lot.parent && thisNode.id === lot.parent.id) { _isHideThisNode = true; } }); thisNode.opacity = _isHideThisNode ? 1 : 0.1; }); } else { // 父级及本身高亮 _all_nodes.forEach((thisNode) => { let _isHideThisNode = false; if (thisNode.id === nodeObject.id) { _isHideThisNode = true; } if (lot.parent && thisNode.id === lot.parent.id) { _isHideThisNode = true; } thisNode.opacity = _isHideThisNode ? 1 : 0.1; }); } }, /** * 线条点击 * @date 2022-10-21 * @param {any} lineObject * @param {any} $event * @returns {any} */ onLineClick(lineObject, $event) { console.log('线条数据:', lineObject); }, /** * 鼠标移入显示提示 * @date 2022-10-21 * @param {any} nodeObject * @param {any} $event * @returns {any} */ showNodeTips(nodeObject, $event) { this.currentNode = nodeObject; const _base_position = this.$refs.graph.getBoundingClientRect(); this.isShowNodeTipsPanel = true; this.nodeMenuPanelPosition.x = $event.clientX - _base_position.x + 90; this.nodeMenuPanelPosition.y = $event.clientY - _base_position.y - 20; }, /** * 鼠标移出隐藏提示 * @date 2022-10-21 * @param {any} nodeObject * @param {any} $event * @returns {any} */ hideNodeTips(nodeObject, $event) { this.isShowNodeTipsPanel = false; }, /** * 点击图谱 * @date 2022-10-21 * @returns {any} */ canvasClick() { const _all_nodes = this.$refs['seeksRelationGraph'].getNodes(); _all_nodes.forEach((thisNode) => { let _isHideThisNode = true; thisNode.opacity = _isHideThisNode ? 1 : 0.1; }); } } }; </script>
5、scss样式:
<style lang="scss" scoped> .graph { position: relative; } .node { height: 100%; } .company-node { width: 100%; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 3; overflow: hidden; line-height: 17px; cursor: pointer; } .human-node { height: 100%; display: flex; align-items: center; justify-content: center; cursor: pointer; } .node-dialog { z-index: 9999; padding: 10px; background-color: #ffffff; border: #eeeeee solid 1px; box-shadow: 0px 0px 8px #cccccc; position: absolute; } </style>
6、最终效果:
希望大佬看到有不对的地方,提出博主予以改正!