记录一个需求,做一个族谱,就想到了eacharts的关系图,不用自己写canvas,适当配置就可以达到还不错的效果。比如添加头像,点击添加子代,缩放,拖拽(后来我关闭了),移动等话不多说了,上代码
1、下载eacharts (eacharts官方文档Apache ECharts)
npm install echarts
2、项目里引入eacharts(我这是vue2的项目,因为项目用图标比较多,所以使用的全局引入,有需要的可以单独引入或者按需引入,都在官方文档里了我就不多说了)
// 引入echarts
import echarts from 'echarts'
Vue.prototype.$echarts = echarts
3、html部分
<template>
<div id="family-tree" style="width: 100%; height: 600px;"></div>
</template>
4、js(methods)部分 eacharts配置文档(Documentation - Apache ECharts)
renderFamilyTree() {
const chart = this.$echarts.init(document.getElementById('family-tree'))
const option = {
tooltip: {
},
xAxis: {
type: 'category',
splitLine: {
show: false
}
},
yAxis: {
type: 'category',
data: ["第一代", "第二代", "第三代", "第四代", "第五代"],
splitLine: {
show: true
}
},
series: [
{
type: 'graph',//关系图
layout: 'none',
xAxisIndex:0,
yAxisIndex:0,
symbolSize: 50,
edgeSymbol: ['circle', 'arrow'],
center: [130, 160],
label:{
position: "bottom"
},
emphasis: {
focus: 'adjacency',
blurScope: 'coordinateSystem',
lineStyle: {
width: 2,
},
itemStyle: {
borderWidth: 10,
borderType: [5, 10],
borderDashOffset: 5
},
},
// draggable: true,//拖拽
edgeLabel:{
// show:true //标签显示
},
roam:'move',//移动
// 缩放比例
scaleLimit:{
min:0.4,
max:2,
},
edgeSymbolSize: [4, 10],
data: [
{ name: '张爷爷', value:100,x: 50, y: 100 , label: { show: true },symbol: 'image://https://backupfie.empowerchint.com/1687876059-compressed-IMG_00901700556145090.JPG'},
{ name: '代奶奶',value:98, x: 70, y: 100 , itemStyle: { color: 'lightblue' }, label: { show: true } },
{ name: '张爸爸',value:66, x: 50, y: 130 , label: { show: true },},
{ name: '张叔叔',value:65, x: 150, y: 130 ,label: { show: true }},
{ name: '何大婶',value:64, x: 170, y: 130 , itemStyle: { color: 'lightblue' }, label: { show: true } },
{ name: '何二婶',value:63, x: 190, y: 130 , itemStyle: { color: 'lightblue' }, label: { show: true } },
{ name: '张亲姐',value:40, x: 20, y: 160 ,label: { show: true }},
{ name: '张自己',value:39, x: 50, y: 160 ,label: { show: true }},
{ name: '张大婶弟', value:34,x: 150, y: 160 ,label: { show: true }},
{ name: '张二婶妹', value:32,x: 250, y: 160 ,label: { show: true }},
{ name: '张二婶弟', value:31,x: 300, y: 160 ,label: { show: true }},
{ name: '张二婶弟媳', value:31,x: 320, y: 160 ,label: { show: true },itemStyle: { color: 'lightblue' }},
{ name: '张二婶妹亲侄',value:18, x: 250, y: 190 ,label: { show: true }},
{ name: '张儿子', x: 50,value:18, y: 190,label: { show: true }},
{ name: '赵儿媳', x: 70,value:17, y: 190 , itemStyle: { color: 'lightblue' }, label: { show: true } },
{ name: '张大婶弟侄女',value:16, x: 150, y: 190 ,label: { show: true }},
{ name: '张孙子', x: 50,value:1, y: 220 ,label: { show: true }},
],
links: [
{
source: '张爷爷',
target: '张爸爸' ,
label:{
show:false,
formatter: ['{11111111}',].join('\n')
}
},
{ source: '代奶奶', target: '张爸爸' },
{ source: '张爷爷', target: '张叔叔' },
{ source: '代奶奶', target: '张叔叔' },
{ source: '张爸爸', target: '张亲姐' },
{ source: '张爸爸', target: '张自己' },
{ source: '张叔叔', target: '张大婶弟' },
{ source: '何大婶', target: '张大婶弟' },
{ source: '张叔叔', target: '张二婶妹' },
{ source: '何二婶', target: '张二婶妹' },
{ source: '张自己', target: '张儿子' },
{ source: '张二婶妹', target: '张二婶妹亲侄' },
{ source: '张大婶弟', target: '张大婶弟侄女' },
{ source: '张儿子', target: '张孙子' },
{ source: '赵儿媳', target: '张孙子' },
],
},
],
};
chart.setOption(option);
chart.on('dblclick', (params) => {
if (params.dataType === 'node') {
// 在此处处理双击节点弹出提示框的逻辑
console.log('双击了节点:', params.data.name);
}
});
chart.on('click', (params) => {
if (params.dataType === 'edge') {
const source = params.data.source;
const target = params.data.target;
console.log(chart.getOption().series[0].links)
// 弹出输入框让用户输入标签
const label = prompt('请输入连线的标签:');
// 将标签信息保存到连线的数据中
const allData = chart.getOption().series[0].links
const edgeData = allData.find((link) => link.source === source && link.target === target);
const edgeIndex = allData.findIndex((link) => link.source === source && link.target === target);
if (edgeData&&edgeIndex>-1) {
edgeData.label = {
show:true,
formatter :[label].join('\n')
}
allData[edgeIndex] = edgeData
chart.setOption({ series: [{ links:allData}] });
console.log(chart.getOption().series[0].links)
}
}
});
chart.on('click', (params) => {
if (params.dataType === 'node') {
const node = params.data.name;
// 弹出菜单让用户选择增加子节点还是删除子节点
const choice = confirm(`您想要对节点 ${node} 进行什么操作?\n\n1. 增加子节点\n2. 删除子节点`);
if (choice) {
// 用户选择增加子节点
const newName = prompt('请输入新节点的名称:');
const allData = chart.getOption().series[0].data
const nodeIndex = allData.findIndex((data) => data.name === node);
const newNode = { name: newName, x: params.data.x, y: params.data.y+30, label: { show: true } }
const allLinks = chart.getOption().series[0].links
const newAllLinks= [...allLinks,{source: node, target: newName }]
allData.splice(nodeIndex,0,newNode)
chart.setOption({ series: [{ data:allData}] });
chart.setOption({ series: [{links:newAllLinks}] });
} else {
// 用户选择删除子节点
const allData = chart.getOption().series[0].data
const nodeData = allData.find((data) => data.name === node);
const nodeIndex = allData.findIndex((data) => data.name === node);
const allLinks = chart.getOption().series[0].links
allData.splice(nodeIndex,1)
console.log(allData,nodeData,nodeIndex)
chart.setOption({ series: [{ data:allData}] });
// const links = chart.getOption().series[0].links.filter((link) => link.source === node || link.target === node);
// const data = chart.getOption().series[0].data.filter((item) => item.name !== node);
// chart.setOption({ series: [{ data, links }] });
}
}
});
},
还有很多需要优化的,不过大概是这样子