react中使用echarts关系图

news/2024/11/15 18:45:59/文章来源:https://www.cnblogs.com/class1/p/13692539.html

 

一,工作需求,展示几类数据关系,可缩放大小,可拖拽位置,在节点之间的连线上展示相关日期,每个节点展示本身信息,并且要求每个关系节点能点击。

实现情况如图所示:

二,实现过程中遇到的问题: 关系图完美呈现,但关系节点点击后,整个关系图会杂乱无章的浮动,导致不知道点击了哪个关系节点。

 

 

三,解决思路,等关系图稳定后,获取每个关系节点的位置坐标,再次将关系节点定在相应的位置。

四,整体代码:

相关参数options.js

export default {title: {text: '综合关系图(以GID为中心)',},tooltip: {backgroundColor: '#666',formatter(params) {//  console.log("wq",params)return `<div>${params.name}</div>
      `;},},animationDurationUpdate: 1000, // 1500animationEasingUpdate: 'quinticInOut',label: {normal: {show: true,textStyle: {fontSize: 12,},},},legend: {x: 'center',show: true,data: ['GID', 'UID', 'DID', 'IP', 'PHONE'],},series: [{type: 'graph',layout: 'force',symbolSize: 35, // 关系节点大小focusNodeAdjacency: true, // 是否在鼠标移到节点上的时候突出显示节点以及节点的边和邻接节点。roam: true, // 是否开启鼠标缩放和平移漫游 可以设置成 'scale' 或者 'move'。设置成 true 为都开启
      categories: [{// 节点分类类目name: 'GID',itemStyle: {normal: {color: '#C23531',},},},{name: 'UID',itemStyle: {normal: {color: '#2F4554',},},},{name: 'DID',itemStyle: {normal: {color: '#F9AB3F',},},},{name: 'IP',itemStyle: {normal: {color: '#6F9B03',},},},{name: 'PHONE',itemStyle: {normal: {color: '#1890FF',},},},],label: {normal: {show: true,textStyle: {fontSize: 12,},},},force: {repulsion: 1000, // 节点之间的斥力因子。支持数组表达斥力范围,值越大斥力越大。gravity: 0.03, // 节点受到的向中心的引力因子。该值越大节点越往中心点靠拢。edgeLength: 160, // 边的两个节点之间的距离,这个距离也会受 repulsion。[10, 50] 。值越小则长度越长layoutAnimation: true, // 这个参数决定是否显示布局的迭代动画,在浏览器端节点数据较多(>100)的时候不建议关闭,布局过程会造成浏览器假死。
      },// edgeSymbolSize: [4, 50],
      edgeLabel: {normal: {show: true,textStyle: {fontSize: 10,},formatter: '{c}',},},data: [],links: [],lineStyle: {normal: {opacity: 0.9,width: 1,curveness: 0,},},},],
};

 

实现页代码:在react代码中实现的关系图

import React, { PureComponent } from 'react';
import { Card, Form, Modal, } from 'antd';
import { connect } from 'dva';
import echarts from 'echarts/lib/echarts';
import PageHeaderWrapper from '../../../components/PageHeaderWrapper';
import IpMoudle from './ipMoudle';
import GidMoudle from './gidMoudle';
import UidMoudle from './accountMoudle';
import DidMoudle from './didMoudle';
import PhoneMoudle from './phoneMoudle';
import 'echarts/lib/chart/graph';
import 'echarts/lib/component/tooltip';
import 'echarts/lib/component/legend';
import 'echarts/lib/component/title';
import options from './options';

const getzf = val => {
return val * 1 < 10 ? `0${val}` : val;
};

const shortFormatDate = d => {
if (d === undefined) {
return '';
}
const now = new Date(d * 1000);
const year = now.getFullYear();
const month = now.getMonth() + 1;
const date = now.getDate();
return `${year}/${getzf(month)}/${getzf(date)}`;
};
@connect(({ allVal, loading }) => ({
allVal,
loading: loading.models.allVal,
}))
@Form.create()
class Over extends PureComponent {
constructor(props) {
super(props);
this.state = {
start: '',
end: '',
flog: 'gid',
lei: '',
};
}

componentDidMount() {
const myChart = echarts.init(document.getElementById('main'));
// 节点绑定点击事件
myChart.on('click', params => {
this.openShow(params);
});
this.getCharts();
}

getCharts = () => {
const myChart = echarts.init(document.getElementById('main'));
const {
allVal: { baseData },
} = this.props;
// baseData: [
// {uid: "1553919784", gid: "000000002548ba6e", create_time: 1595606400, update_time: 1638979200},
// {ip: "123.157.158.59", gid: "000000002548ba6e", create_time: 1601049600, update_time: 1639065600},
// {did: "EFBA0B48-4B7E-42B1-9D5D-B33359C3AEDF", gid: "000000015508f569", create_time: 1609516800,update_time: 1642608000},
// {uid: "1553919784", gid: "000000015508f569", create_time: 1612454400, update_time: 1642608000},
// {did: "ANDROID_04b0673ccef36422", gid: "000000002548ba6e", create_time: 1634227200,update_time: 1638979200},
// {ip: "112.10.61.105", gid: "000000015508f569", create_time: 1636214400, update_time: 1638547200},
// {ip: "115.195.90.61", gid: "000000015508f569", create_time: 1637164800, update_time: 1637164800},
// {ip: "60.163.254.188", gid: "000000015508f569", create_time: 1638460800, update_time: 1638460800},
// {ip: "122.231.208.82", gid: "000000015508f569", create_time: 1638806400, update_time: 1638806400},
// {did: "ANDROID_04b0673ccef36423", gid: "000000002548ba6e", create_time: 1638979200,update_time: 1639065600},
// {ip: "115.220.137.100", gid: "000000015508f569", create_time: 1638979200, update_time: 1639238400},
// {ip: "39.182.14.22", gid: "000000015508f569", create_time: 1641657600, update_time: 1641657600},
// {ip: "223.104.19.214", gid: "000000015508f569", create_time: 1642608000, update_time: 1642608000},
// ],

const newdata = this.getNewData(baseData); // 处理点的数据
const newlinks = this.getNewlinks(baseData); // 处理节点之间的关系

options.series[0].data = newdata;
options.series[0].links = newlinks;

// 绘制图表
myChart.setOption(options);
   // 获取图表固定坐标,再次绘制图表
    const layout = myChart._chartsViews[0]._symbolDraw._data._itemLayouts;

layout.map((item, index) => {
newdata[index].x = item[0];
newdata[index].y = item[1];
});
options.series[0].data = newdata;
myChart.setOption(options);
   // 节点被选中的操作

myChart.on('legendselectchanged', params => {
      // const { selected } = params;
// ...
});
};

// 再次获取关系节点数据后重绘,暂未调用,有需要再次重绘的可以调用
handleSubmit = e => {
e.preventDefault();
const {
form: { validateFields },
} = this.props;
validateFields((err, values) => {
if (!err) {
const { dispatch } = this.props;
const { idtype, idvalue } = values;
const { start, end } = this.state;
const date1 = new Date(start);
const date2 = new Date(end);
const s = date1.getTime() / 1000;
const en = date2.getTime() / 1000;
const val = { idtype, idvalue, s, en };
if (idtype === 'phone') {
dispatch({
type: 'allVal/fetchAllValP',
payload: val,
});
} else {
dispatch({
type: 'allVal/fetchAllVal',
payload: val,
});
}
}
});
setTimeout(this.getCharts, 1000);
};

// 节点点击触发函数
openShow = e => {
if (e.dataType === 'node') {
const { data } = e;
this.showSelect(data);
}
};

// 根据节点类型不同请求不同的接口 ["GID", "UID", 'DID','IP']
showSelect = data => {
const { category, name } = data;
switch (category) {
case 'GID':
this.gidSearch(name);
break;
case 'UID':
this.uidSearch(name);
break;
case 'IP':
this.ipSearch(name);
break;
case 'PHONE':
this.phoneSearch(name);
break;
case 'DID':
this.didSearch(name);
break;
default:
break;
}
};

ipSearch = name => {
const { dispatch } = this.props;
dispatch({
type: 'allVal/fetchIpVal',
payload: name,
});
this.setState({ lei: 'ip' });
this.showModal();
};

phoneSearch = phone => {
const { dispatch } = this.props;
dispatch({
type: 'allVal/fetchPhoneVal',
payload: { phone },
});
this.setState({ lei: 'phone' });
};

gidSearch = name => {
const { dispatch } = this.props;
dispatch({
type: 'allVal/fetchGidVal',
payload: name,
});
this.setState({ lei: 'gid' });
this.showModal();
};

uidSearch = name => {
const { dispatch } = this.props;
dispatch({
type: 'allVal/fetchUidVal',
payload: { phone: name },
});
this.setState({ lei: 'uid' });
this.showModal();
};

didSearch = name => {
const { dispatch } = this.props;
dispatch({
type: 'allVal/fetchDidVal',
payload: { did: name },
});
this.setState({ lei: 'did' });
this.showModal();
};

// 模态框展示
showModal = () => {
const { dispatch } = this.props;
dispatch({
type: 'allVal/showM',
payload: true,
});
};

// 模态框隐藏
handleCancel = () => {
const { dispatch } = this.props;
dispatch({
type: 'allVal/showM',
payload: false,
});
};

// 判断节点类目
getGrade = (val, b) => {
const a = Object.keys(val);
const c = a.indexOf(b);
a.splice(c, 1);
let grade = '';
for (let i = 0; i < a.length; i += 1) {
switch (a[i]) {
case 'gid':
if (a.indexOf('gid') > -1) {
grade = 'GID';
}
break;
case 'did':
if (a.indexOf('did') > -1) {
grade = 'DID';
}
break;
case 'uid':
if (a.indexOf('uid') > -1) {
grade = 'UID';
}
break;
case 'ip':
if (a.indexOf('ip') > -1) {
grade = 'IP';
}
break;
case 'phone':
if (a.indexOf('phone') > -1) {
grade = 'PHONE';
}
break;
default:
break;
}
}
return grade;
};

// 处理成需要的data格式
getNewData = val => {
const { flog } = this.state;
if (val.length === 0) {
return [];
}
const arr = val.map(item => item.gid);
const newArr = Array.from(new Set(arr));
const newdata = [];
// eslint-disable-next-line array-callback-return
newArr.map(item => {
newdata.push({
name: item,
draggable: false,
label: {
show: false,
},
category: 'GID',
});
});
// eslint-disable-next-line array-callback-return
val.map(item => {
newdata.push({
name: item[this.getGrade(item, flog).toLowerCase()],
draggable: false,
label: {
show: false,
},
category: this.getGrade(item, flog),
});
});
const hash = {};
const data = newdata.reduce(function(item, next) {
// eslint-disable-next-line no-unused-expressions
hash[next.name] ? '' : (hash[next.name] = true && item.push(next));
return item;
}, []);
return data;
};

// 处理成需要的links 格式
getNewlinks = val => {
const { flog } = this.state;
if (val.length === 0) {
return [];
}
const newlinks = [];
// eslint-disable-next-line array-callback-return
val.map(item => {
newlinks.push({
source: item[this.getGrade(item, flog).toLowerCase()],
target: item[flog],
value: `${shortFormatDate(item.create_time)}-${shortFormatDate(item.update_time)}`,
});
});
return newlinks;
};


render() {
const { lei } = this.state;
const {
allVal: { visible },
} = this.props;
return (
<PageHeaderWrapper>
<Card style={{ position: 'relative' }}>
<div id="main" style={{ height: '1000px', width: '100%' }}>
{' '}
</div>
<Modal
visible={visible}
title="详细信息"
onCancel={this.handleCancel}
footer={null}
width="800px"
>
<div>
{lei === 'gid' && <GidMoudle />}
{lei === 'ip' && <IpMoudle />}
{lei === 'phone' && <PhoneMoudle />}
{lei === 'uid' && <UidMoudle />}
{lei === 'did' && <DidMoudle />}
</div>
</Modal>
</Card>
</PageHeaderWrapper>
);
}
}

export default Over;

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/792506.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

易基因:中国农大田见晖教授团队揭示DNA甲基化保护早期胚胎线粒体基因组稳定性|项目文章

大家好,这里是专注表观组学十余年,领跑多组学科研服务的易基因。 在早期哺乳动物胚胎中,线粒体氧化代谢增强是着床后生存和发育的重要特征;着床前期的线粒体重塑是正常胚胎发生的关键事件。在这些变化中,氧化磷酸化(OXPHOS)增强对于支持着床后胚胎的高能量需求至关重要,…

WebShell流量特征检测_哥斯拉篇

80后用菜刀,90后用蚁剑,95后用冰蝎和哥斯拉,以phpshell连接为例,本文主要是对这四款经典的webshell管理工具进行流量分析和检测。 什么是一句话木马? 1、定义 顾名思义就是执行恶意指令的木马,通过技术手段上传到指定服务器并可以正常访问,将我们需要服务器执行的命令上…

IPv6基于策略的地址分配

IPv6基于策略的地址分配 RA的周期性发送使用的是组播方式,但是针对 RS 的回复使用组播和单播两种可能;如果 RA 都是以组播方式发送,那么同一个广播域下的所有终端都可以收到,如果要基于终端mac/link-local地址来控制分配策略,则应该使用单播方式回复,以限制RA被接收的范围…

stylus图床

<image src=C:\Users\11277\Downloads\bj.jpg ></image>

djiango module 错误

报错如下实际设置需要加上级包名 通过 apps.py 统一处理本文来自博客园,作者:vx_guanchaoguo0,转载请注明原文链接:https://www.cnblogs.com/guanchaoguo/p/18397956

python 源文件 源目录 转 包

python setup.py sdist 命令会完成以下步骤:准备源码:将源文件(包括 Python 文件、数据文件等)收集到一个目录中,以便打包。生成分发文件:创建一个压缩包(通常是 .tar.gz 或 .zip 格式),包含所有必要的源文件和元数据。这些文件会被放置在 dist 目录中。构建步骤:sdi…

神经网络之卷积篇:详解卷积神经网络示例(Convolutional neural network example)

详解卷积神经网络示例 假设,有一张大小为32323的输入图片,这是一张RGB模式的图片,想做手写体数字识别。32323的RGB图片中含有某个数字,比如7,想识别它是从0-9这10个数字中的哪一个,构建一个神经网络来实现这个功能。用的这个网络模型和经典网络LeNet-5非常相似,灵感也来…

火山引擎VeDI赋能小城酒店业,助力“流量”向“留量”转化

对小城酒店业而言,市场红利期的机遇从天而降。在此背景下,争取以优质服务打动游客、将短暂“流量”化为“留量”,才是其真正实现长足发展的关键一步。而火山引擎数智平台VeDI,或许可以成为小城酒店实现进一步智能升级的助力点。更多技术交流、求职机会,欢迎关注字节跳动数…

网站提示“PHP配置错误:如内存限制、执行时间限制等问题”错误如何解决

当您遇到“PHP配置错误”,如内存限制、执行时间限制等问题时,这通常意味着您的PHP脚本超过了PHP.ini配置文件中设定的某些限制。这类问题可能导致脚本执行中断或产生错误。以下是一些解决这些问题的方法: 常见的PHP配置错误内存限制(memory_limit):当PHP脚本消耗的内存超…

Java接口使用指南:开启高效编程之门

https://img2024.cnblogs.com/blog/3506472/202409/3506472-20240905103248440-1956252110.png在Java编程世界中,接口是实现模块间通信的一种核心机制。它们定义了一组方法规范,允许不同的类或系统按照统一的方式进行交互。随着互联网服务的兴起,API(应用程序编程接口)成为…

网站提示“Duplicate entry:插入重复记录”错误如何解决

当您遇到“Duplicate entry:插入重复记录”的错误时,这意味着您尝试向数据库中插入的数据违反了唯一性约束。这种情况通常发生在以下几种情形下:主键索引重复:尝试插入的记录的主键值与表中已有的主键值相同。 唯一索引重复:如果表中有一个或多个列被定义为具有唯一约束的…