或者有没有佬推荐另外一个关系数据展示组件
6 Likes
推荐一个
2 Likes
多谢 我研究下
2 Likes
要那个demo,发下地址,我拷给你
佬 这个
1 Like
插个眼,没写过这个需求
1 Like
vue2还是vue3的
vue2
<template>
<div>
<div style="height: calc(100vh - 50px)">
<RelationGraph
ref="graphRef"
:options="graphOptions"
:on-contextmenu="onContextmenu"
>
<template #graph-plug>
<div
v-if="isShowNodeTipsPanel"
:style="{left: nodeMenuPanelPosition.x + 'px', top: nodeMenuPanelPosition.y + 'px' }"
class="c-right-menu-panel"
>
<div class="c-object-info">
<div>当前右键事件信息:</div>
<div>类型:{{currentObjectType}}</div>
<div v-if="currentObjectType==='link'">{{currentObject.fromNode.text}} -> {{currentObject.toNode.text}}</div>
<div v-if="currentObjectType==='node'">ID:{{currentObject.id}}</div>
<div v-if="currentObjectType==='node'">Text:{{currentObject.text}}</div>
<div>你可以对这个对象做以下操作:</div>
</div>
<div v-if="currentObjectType==='canvas'" class="c-node-menu-item" @click="addNode">添加节点</div>
<div v-if="currentObjectType==='node'" class="c-node-menu-item" @click="deleteNode">删除节点</div>
<div v-if="currentObjectType==='node'" class="c-node-menu-item" @click="createLineFromNode">添加连线</div>
<div v-if="currentObjectType==='link'" class="c-node-menu-item" @click="deleteLink">删除关系</div>
</div>
</template>
</RelationGraph>
</div>
</div>
</template>
<script>
// 如果您没有在main.js文件中使用Vue.use(RelationGraph); 就需要使用下面这一行代码来引入relation-graph
// import RelationGraph from "relation-graph";
const graphOptions = {
allowSwitchLineShape: true,
allowSwitchJunctionPoint: true,
allowShowDownloadButton: true,
defaultJunctionPoint: 'border'
};
export default {
name: 'ObjectEdit',
components: { },
data() {
return {
isShowNodeTipsPanel: false,
nodeMenuPanelPosition: { x: 0, y: 0 },
currentObjectType: null,
currentObject: '',
newNodeIdIndex: 1,
newLineIdIndex: 1,
graphOptions
};
},
mounted() {
this.showGraph();
},
methods: {
showGraph() {
const __graph_json_data = {
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', text: '', color: '#67C23A' }
]
};
const graphRef = this.$refs.graphRef
graphRef.setJsonData(__graph_json_data, (graphInstance) => {
// 这些写上当图谱初始化完成后需要执行的代码.
}
);
},
onContextmenu($event, objectType, object) {
const graphInstance = this.$refs.graphRef.getInstance();
this.currentObjectType = objectType;
this.currentObject = object;
const _base_position = graphInstance.getBoundingClientRect();
console.log('showNodeMenus:', $event, _base_position);
this.isShowNodeTipsPanel = true;
this.nodeMenuPanelPosition.x = $event.clientX - _base_position.x + 10;
this.nodeMenuPanelPosition.y = $event.clientY - _base_position.y + 10;
const hideContentMenu = () => {
this.isShowNodeTipsPanel = false;
document.body.removeEventListener('click', hideContentMenu)
}
document.body.addEventListener('click', hideContentMenu)
},
addNode($event) {
const graphInstance = this.$refs.graphRef.getInstance();
const _base_position = graphInstance.getBoundingClientRect();
const canvasCoordinate = graphInstance.getCanvasCoordinateByClientCoordinate({
x: this.nodeMenuPanelPosition.x - 10 + _base_position.x,
y: this.nodeMenuPanelPosition.y - 10 + _base_position.y
});
const newId = this.newNodeIdIndex++
graphInstance.addNodes([{
id: 'addFromCanvas-' + newId,
text: 'New-' + newId,
color: '#5da0f8',
x: canvasCoordinate.x,
y: canvasCoordinate.y
}]);
},
deleteNode($event) {
const graphInstance = this.$refs.graphRef.getInstance();
graphInstance.removeNodeById(this.currentObject.id);
},
deleteLink($event) {
const graphInstance = this.$refs.graphRef.getInstance();
graphInstance.removeLinkById(this.currentObject.seeks_id);
},
createLineFromNode(e) {
const graphInstance = this.$refs.graphRef.getInstance();
graphInstance.startCreatingLinePlot(e, {
template: {
lineWidth: 3,
color: '#e85f84',
text: '新连线'
},
fromNode: this.currentObject,
onCreateLine: (from, to) => {
console.log('新增连线:', from, to);
if (to.id) { // 创建的连线的起点一定是节点,但终点可以是空白处,终点没有选择成节点时to不是一个节点,to.id不会有值,这里做了判断,只处理to为节点的情况
const newLineId = this.newLineIdIndex++;
graphInstance.addLines([{
from: from.id,
to: to.id,
lineWidth: 3,
color: '#e85f84',
text: '新连线' + newLineId
}]);
}
}
});
}
},
};
</script>
<style>
</style>
<style scoped>
.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);
}
.c-object-info{
line-height: 18px;padding-left: 10px;color: #888888;font-size: 12px;
}
.c-right-menu-panel{
z-index: 999;padding:10px;background-color: #ffffff;border:#eeeeee solid 1px;box-shadow: 0px 0px 8px #cccccc;position: absolute;
border-radius: 10px;
width:200px;
}
</style>
vue3
<template>
<div>
<div style="height: calc(100vh)">
<RelationGraph ref="graphRef" :options="graphOptions" @contextmenu="onContextmenu">
<template #graph-plug>
<div class="c-my-panel">
<div class="c-option-name">Please try right-clicking on the artboard, node, or line</div>
</div>
<div
v-if="isShowNodeTipsPanel"
:style="{ left: nodeMenuPanelPosition.x + 'px', top: nodeMenuPanelPosition.y + 'px' }"
class="c-right-menu-panel"
>
<div class="c-object-info">
<div>Current right-click event information:</div>
<div>Type: {{ currentObjectType }}</div>
<div v-if="currentObjectType === 'link'">
{{ currentObject.fromNode.text }} -> {{ currentObject.toNode.text }}
</div>
<div v-if="currentObjectType === 'node'">ID: {{ currentObject.id }}</div>
<div v-if="currentObjectType === 'node'">Text: {{ currentObject.text }}</div>
<div>You can perform the following operations on this object:</div>
</div>
<div v-if="currentObjectType === 'canvas'" class="c-node-menu-item" @click="addNode">
Add Node
</div>
<div v-if="currentObjectType === 'node'" class="c-node-menu-item" @click="deleteNode">
Delete Node
</div>
<div v-if="currentObjectType === 'node'" class="c-node-menu-item" @click="createLineFromNode">
Add Link
</div>
<div v-if="currentObjectType === 'link'" class="c-node-menu-item" @click="deleteLink">
Delete Relation
</div>
</div>
</template>
</RelationGraph>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue';
import RelationGraph from 'relation-graph-vue3';
import type { RGJsonData, RGNode, RGLine, RGLink, RGUserEvent, RGOptions, RelationGraphComponent } from 'relation-graph-vue3';
const graphOptions: RGOptions = {
allowSwitchLineShape: true,
allowSwitchJunctionPoint: true,
allowShowDownloadButton: true,
defaultJunctionPoint: 'border'
};
const isShowNodeTipsPanel = ref(false);
const nodeMenuPanelPosition = ref({ x: 0, y: 0 });
const currentObjectType = ref(null);
const currentObject = ref('');
let newNodeIdIndex = 1;
let newLineIdIndex = 1;
const showGraph = async() => {
const __graph_json_data: RGJsonData = {
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: 'Relation 1', color: '#43a2f1' },
{ from: 'a', to: 'c', text: 'Relation 2' },
{ from: 'a', to: 'e', text: 'Relation 3' },
{ from: 'b', to: 'e', text: '', color: '#67C23A' }
]
};
const graphInstance = graphRef.value?.getInstance();
if (graphInstance) {
await graphInstance.setJsonData(__graph_json_data);
await graphInstance.moveToCenter();
await graphInstance.zoomToFit();
}
};
const onContextmenu = ($event: RGUserEvent, objectType: string, object: any) => {
const graphInstance = graphRef.value.getInstance();
currentObjectType.value = objectType;
currentObject.value = object;
const _base_position = graphInstance.getBoundingClientRect();
console.log('showNodeMenus:', $event, _base_position);
isShowNodeTipsPanel.value = true;
nodeMenuPanelPosition.value.x = $event.clientX - _base_position.x + 10;
nodeMenuPanelPosition.value.y = $event.clientY - _base_position.y + 10;
const hideContentMenu = () => {
isShowNodeTipsPanel.value = false;
document.body.removeEventListener('click', hideContentMenu);
};
document.body.addEventListener('click', hideContentMenu);
};
const addNode = ($event: MouseEvent) => {
const graphInstance = graphRef.value.getInstance();
const _base_position = graphInstance.getBoundingClientRect();
const canvasCoordinate = graphInstance.getCanvasCoordinateByClientCoordinate({
x: nodeMenuPanelPosition.value.x - 10 + _base_position.x,
y: nodeMenuPanelPosition.value.y - 10 + _base_position.y
});
const newId = newNodeIdIndex++;
graphInstance.addNodes([
{
id: 'addFromCanvas-' + newId,
text: 'New-' + newId,
color: '#5da0f8',
x: canvasCoordinate.x,
y: canvasCoordinate.y
}
]);
};
const deleteNode = ($event: MouseEvent) => {
const graphInstance = graphRef.value.getInstance();
graphInstance.removeNodeById(currentObject.value.id);
};
const deleteLink = ($event: MouseEvent) => {
const graphInstance = graphRef.value.getInstance();
graphInstance.removeLinkById(currentObject.value.seeks_id);
};
const createLineFromNode = (e: MouseEvent) => {
const graphInstance = graphRef.value.getInstance();
graphInstance.startCreatingLinePlot(e, {
template: {
lineWidth: 3,
color: '#e85f84',
text: 'New Link'
},
fromNode: currentObject.value,
onCreateLine: (from: RGNode, to: RGNode) => {
console.log('New Link:', from, to);
if (to.id) {
const newLineId = newLineIdIndex++;
graphInstance.addLines([
{
from: from.id,
to: to.id,
lineWidth: 3,
color: '#e85f84',
text: 'New Link ' + newLineId
}
]);
}
}
});
};
const graphRef = ref<RelationGraphComponent>();
onMounted(() => {
showGraph();
});
</script>
<style>
.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);
}
.c-object-info {
line-height: 18px;
padding-left: 10px;
color: #888888;
font-size: 12px;
}
.c-right-menu-panel {
z-index: 999;
padding: 10px;
background-color: #ffffff;
border: #eeeeee solid 1px;
box-shadow: 0px 0px 8px #cccccc;
position: absolute;
border-radius: 10px;
width: 200px;
}
.c-my-panel {
position: absolute;
left: 10px;
top: 10px;
border-radius: 10px;
z-index: 800;
background-color: #efefef;
border: #eeeeee solid 1px;
padding: 20px;
.c-option-name{
color: #666666;
font-size: 14px;
line-height: 40px;
padding-left:10px;
padding-right:10px;
}
.c-my-button{
border-radius: 5px;
height: 42px;
width: 150px;
text-align: center;
line-height: 30px;
padding:5px;
cursor: pointer;
background-color: #ffffff;
border:rgba(79, 30, 30, 1) solid 1px;
&:hover {
transition: background-color .2s ease,outline .2s ease,color .2s ease,-webkit-box-shadow .2s ease;
box-shadow: 0 0 0 5px rgba(79, 30, 30, 0.3);
}
}
.c-my-button-checked{
transition: background-color .2s ease,outline .2s ease,color .2s ease,-webkit-box-shadow .2s ease;
box-shadow: 0 0 0 5px rgba(79, 30, 30, 0.3);
background-color: rgba(79, 30, 30, 1);
color: #ffffff;
}
}
</style>
太感谢佬了,Vue2的就行
这个能帮忙拷下吗
<template>
<div>
<div style="height: calc(100vh - 50px)">
<RelationGraph
ref="graphRef"
:options="graphOptions"
>
<template #tool-bar>
<ObjectEditToolBar />
</template>
</RelationGraph>
</div>
</div>
</template>
<script>
// 如果您没有在main.js文件中使用Vue.use(RelationGraph); 就需要使用下面这一行代码来引入relation-graph
// import RelationGraph from "relation-graph";
import ObjectEditToolBar from './VipObjectEditToolBar';
const graphOptions = {
debug: false,
allowSwitchLineShape: true,
allowSwitchJunctionPoint: true,
allowShowDownloadButton: true,
defaultJunctionPoint: 'border'
};
export default {
name: 'ObjectEdit',
components: { ObjectEditToolBar },
data() {
return {
isShowNodeTipsPanel: false,
nodeMenuPanelPosition: { x: 0, y: 0 },
currentObjectType: null,
currentObject: '',
newNodeIdIndex: 1,
newLineIdIndex: 1,
graphOptions
};
},
mounted() {
this.showGraph();
},
methods: {
showGraph() {
const __graph_json_data = {
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', text: '', color: '#67C23A' }
]
};
const graphRef = this.$refs.graphRef
graphRef.setJsonData(__graph_json_data, (graphInstance) => {
// 这些写上当图谱初始化完成后需要执行的代码.
}
);
}
},
};
</script>
<style>
</style>
<style lang="scss" scoped>
::v-deep .relation-graph {
.my-node-template{
//transform: translateX(-60px) translateY(-60px) !important;
cursor: default;
}
}
.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>
佬 少个VipObjectEditToolBar这个
简单的关联图就没必要用这个了吧,后续维护还不好整。echart上面能满足的话,就用echart还好
<template>
<div style="position: absolute;z-index: 30;top:10px;left: 40px; padding:10px;width:400px;height:70px;background-color: #ffffff;border:#efefef solid 1px;box-shadow: 0 3px 9px rgba(0,21,41,.08);border-radius: 10px;">
<div style="display: flex;justify-content: center;place-items: center;gap: 5px;">
<div class="c-mb-button" style="line-height:40px;font-size: 12px;text-align: center;" @click="zoomToFit">
{{ options.canvasZoom }}%
</div>
<div :class="{'c-mb-button-on':options.creatingNodePlot}" class="c-mb-button c-mb-button-c" style="width: 80px;" @click="startAddNode">
<i class="el-icon-football" />
<div class="c-mb-text">节点(点击)</div>
</div>
<div :class="{'c-mb-button-on':options.creatingNodePlot}" class="c-mb-button c-mb-button-c" style="width: 80px;" @mousedown="startAddNode">
<i class="el-icon-football" />
<div class="c-mb-text">节点(拖拽)</div>
</div>
<div :class="{'c-mb-button-on':options.creatingLinePlot}" class="c-mb-button c-mb-button-c" style="width: 50px;" @click="startAddLine">
<i class="el-icon-share" />
<div class="c-mb-text">线条</div>
</div>
<div class="c-mb-button" @click="zoomToFit">
<i class="el-icon-full-screen" />
<div class="c-mb-text">适应</div>
</div>
<div class="c-mb-button" @click="refresh">
<i class="el-icon-refresh-right" />
<div class="c-mb-text">刷新</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'ObjectEditToolBar',
data() {
return {
showNewNodeTemplate: false,
newObjectTemplatePosition: {x: 0, y: 0},
height: 275,
newNodeIdIndex: 1,
newLineIdIndex: 1
};
},
inject: ['graph', 'graphInstance'],
computed: {
relationGraph() {
return this.graphInstance();
},
options() {
return this.graph.options;
}
},
mounted() {
if (this.options.layouts.length > 1) {
this.height -= 40;
}
},
methods: {
about() {
alert('Welcome to Apple Products !');
},
refresh() {
this.relationGraph.refresh();
},
switchLayout(layoutConfig) {
this.relationGraph.switchLayout(layoutConfig, this.options);
this.refresh();
},
toggleAutoLayout() {
// devLog('this.options.autoLayouting', this.options.autoLayouting)
this.relationGraph.toggleAutoLayout();
},
downloadAsFile(format) {
// devLog('this.options.autoLayouting', this.options.autoLayouting)
alert('download as :' + format);
},
async zoomToFit() {
await this.relationGraph.setZoom(100);
await this.relationGraph.moveToCenter();
await this.relationGraph.zoomToFit();
},
startAddNode(e) {
this.relationGraph.startCreatingNodePlot(e, {
templateText: 'Node',
templateNode: {
className: 'my-node-template'
},
onCreateNode: (x, y) => {
console.log('新增节点:', x, y);
const newId = this.newNodeIdIndex++
this.relationGraph.addNodes([{
id: 'newNode-' + newId,
text: '新节点' + newId,
color: '#5da0f8',
x: x - 50,
y: y - 50
}]);
}
});
},
startAddLine(e) {
this.$message({type: 'success', message: '点击节点开始创建线条!'})
this.relationGraph.startCreatingLinePlot(e, {
template: {
lineWidth: 3,
color: '#8080ff',
text: '新连线'
},
onCreateLine: (from, to) => {
console.log('新增连线:', from, to);
if (to.id) { // 创建的连线的起点一定是节点,但终点可以是空白处,终点没有选择成节点时to不是一个节点,to.id不会有值,这里做了判断,只处理to为节点的情况
const newLineId = this.newLineIdIndex++;
this.relationGraph.addLines([{
from: from.id,
to: to.id,
lineWidth: 3,
color: '#8080ff',
text: '新连线' + newLineId
}]);
}
}
});
}
}
};
</script>
<style lang="scss" scoped>
@keyframes rotate {
0% { transform: rotate(0deg); }
100% { transform: rotate(1440deg); }
}
.rg-icon {
width: 16px;
height: 16px;
vertical-align: -3px;
fill: currentColor;
overflow: hidden;
}
.c-mb-button{
height:50px;
width:50px;
padding-top:5px;
background-color: #ffffff;
opacity: 1;
text-align: center;
cursor: pointer;
color: #999999;
font-size: 18px;
box-sizing:border-box;
position: relative;
border-radius: 5px;
}
.c-mb-button .c-mb-text{
font-size: 12px;
line-height: 12px;
color: #262626;
}
.c-mb-button-on{
background-color: #2E74B5;
border-top: #2E4E8F solid 1px;
color: #ffffff;
}
.c-mb-button:hover{
background-color: #00bb00;
border-top: #00bb00 solid 1px;
color: #ffffff;
}
.c-mb-button:hover .c-mb-text,.c-mb-button-on .c-mb-text{
color: #ffffff;
}
.c-mb-button .c-mb-child-panel{
height:46px;
position: absolute;
top: 0px;
z-index: 999;
background-color: #ffffff;
display: none;
border: #bbbbbb solid 1px;
box-shadow: 0px 0px 8px #cccccc;
box-sizing:border-box;
}
.c-mb-button:hover .c-mb-child-panel{
display: block;
}
.c-mb-button .c-mb-button{
height:44px;
width: 42px;
margin:0px;
border: none;
}
.c-mb-button-c .c-mb-text{
color: #262626 !important;
}
.c-mb-button-c:hover .c-mb-text,.c-mb-button-on .c-mb-text{
color: #ffffff !important;
}
.c-loading-icon{
animation:turn 1s linear infinite;
}
@keyframes turn{
0%{-webkit-transform:rotate(0deg);}
25%{-webkit-transform:rotate(90deg);}
50%{-webkit-transform:rotate(180deg);}
75%{-webkit-transform:rotate(270deg);}
100%{-webkit-transform:rotate(360deg);}
}
</style>
<style scoped>
</style>
OK,有需要整理好再留言
这插件功能挺好的,就是Demo都要vip 有点头疼 关键是API写的不太好确实没时间看他的源码
是的 当时不用echarts主要考虑节点DOM好操作,后面有动态动节点跟画线等操作
学习了
佬拷个react的
那个demo呀