需求是实现数据调用返回的流程图,时间比较赶,考虑用echarts的关系图来实现,方便快捷。
代码没有整理,按照需求逻辑处理,也没空写demo,记录一下
<template>
<div id="graphEchart" />
</template>
<script>
import moment from 'moment'
import * as echarts from 'echarts'
const OFFSETY = 300 // 偶数
const HEIGHTY = 2000
const NODESPACE = 500
export default {
data() {
return {
lineList: [],
graphEchart: null,
echartOptions: null,
nodeData: [],
linksData: [],
serverAppTypeObj: {
'iar': 'IAR',
'jres-svr': 'JSVR',
'MQ': 'MQ',
'MC': 'MC',
'KAFKA': 'KAFKA',
'REDIS': 'REDIS',
'FAST_DFS': 'FASTDFS'
}
}
},
mounted() {
this.graphEchart = echarts.init(document.getElementById('graphEchart'))
},
methods: {
resetSize() {
this.graphEchart.resize()
},
resetData() {
// 第一个节点,手段创建
this.nodeData = [
{
name: '客户端',
id: '-1',
x: 0,
y: HEIGHTY,
value: {
type: '1'
}
}
]
this.linksData = []
},
initEchart(list) {
if (!list) return
this.resetData()
this.lineList = list
this.hanleData()
},
changeSpanId(spanId) {
const arr = spanId.split('.')
const list = []
arr.forEach(item => {
list.push(item.split('-')[0])
})
return list.join('.')
},
// 处理数据
hanleData() {
const nodeList = []
this.lineList.forEach((item) => {
if (!item.spanId) return
let resultSpanId = ''
if (item.spanId.indexOf('-') === -1) {
resultSpanId = item.spanId
} else {
resultSpanId = this.changeSpanId(item.spanId)
}
// const resultSpanId = this.changeSpanId(item.spanId)
const spanCount = resultSpanId.replace(/\./g, '')
// const spanCount = item.spanId.split('.')
const nodeItem = {
node: item,
value: resultSpanId
// value: item.spanId
}
const index = spanCount.length - 1
if (nodeList[index]) {
const arr = nodeList[index]
arr.push(nodeItem)
} else {
nodeList[index] = [nodeItem]
}
// 添加处理过的spanId,为后续处理node节点关联
item['resultSpanId'] = resultSpanId
})
nodeList.forEach((arr) => {
arr.sort((a, b) => {
return a.value > b.value 1 : -1
})
})
console.log('nodeList:', nodeList)
this.createNode(nodeList)
this.createLink(nodeList)
this.drawEchart()
},
createNode(list) {
let y = HEIGHTY
list.forEach((arr, i) => {
let x = (i + 1) * NODESPACE
let valueY = 0
if (arr.length > 1) {
if (arr.length % 2 === 0) {
valueY = y - ((OFFSETY / 2) * (arr.length - 1))
} else {
valueY = y - ((Math.floor(arr.length / 2)) * (OFFSETY))
}
} else {
valueY = y
}
arr.forEach((item, j) => {
// const appType = this.serverAppTypeObj[item.node.serverAppType]
const nodeItem = {
name: item.node.serverName,
id: item.value,
x: x,
y: valueY + (OFFSETY * j),
label: this.getSeriesDataLabel(item.node),
value: {
type: '2',
nodeName: item.node.serverName,
ip: item.node.serverIp,
srSsTime: item.node.srSsTime / 1000,
timestamp: item.node.timestamp
}
}
this.nodeData.push(nodeItem)
})
})
console.log('nodeData:', this.nodeData)
},
getSeriesDataLabel(item) {
return {
offset: [0, 25],
fontSize: 10,
align: 'center',
formatter: [
// `{a|${this.serverAppTypeObj[item.serverAppType]}}`,
`{a|${this.settingAppType(item.serverAppType)}}`,
`{b|${item.serverName}}`
].join('\n'),
rich: {
a: {
lineHeight: 25
},
b: {
height: 50,
color: '#303133',
borderColor: '',
fontweight: 100
}
}
}
},
settingAppType(type) {
if (!type) {
return ''
}
if (type === 'jres-svr') {
return 'JSVR'
} else if (type === 'FAST_DFS') {
return 'FASTDFS'
}
return type.toUpperCase()
},
getToolTip() {
return {
tigger: 'item',
enterable: true,
position: 'right',
formatter: (params) => {
const { value } = params.data
if (value.type === '1') {
return `<p>节点名称:客户端</p>`
}
if (value.type === '3') {
return `<div>
<p>服务名:${value.des}</p>
</div>`
}
if (value.type === '2') {
return `<div>
<p>节点名称:${value.nodeName}</p>
<p>ip: ${value.ip value.ip : ''}</p>
<p>服务处理耗时:${value.srSsTime} ms</p>
<p>时间戳:${moment(value.timestamp).format('YYYY-MM-DD HH:mm:ss')}</p>
</div>`
// <p>调用次数:${value.srSsTime}</p>
}
}
}
},
createLink(nodeList) {
// lineList
const list = JSON.parse(JSON.stringify(this.lineList))
list.sort((a, b) => {
return a.resultSpanId > b.resultSpanId 1 : -1
})
list.forEach(item => {
console.log(item.resultSpanId)
})
let index = 0
list.forEach((item, i, arr) => {
if (i === 0) {
index++
this.linksData.push(
this.getLinkObj('-1', item.resultSpanId, item.callStr, index)
)
this.linksData.push(
this.getLinkObj(item.resultSpanId, '-1')
)
} else {
const lastIndex = item.resultSpanId.lastIndexOf('.')
const forwardSpanId = item.resultSpanId.substr(0, lastIndex)
const forwardNode = arr.filter(obj => {
return obj.resultSpanId === forwardSpanId
})[0]
if (forwardNode) {
index++
this.linksData.push(
this.getLinkObj(forwardNode.resultSpanId, item.resultSpanId, item.callStr, index)
)
this.linksData.push(
this.getLinkObj(item.resultSpanId, forwardNode.resultSpanId)
)
}
}
})
},
getLinkObj(source, target, callStr, i) {
return {
source: source,
target: target,
label: {
show: callStr,
fontSize: 10,
offset: [0, 4],
formatter: [
`{a|${i}.服务名}`,
`{b|${callStr}}`
].join('\n'),
rich: {
a: {
color: '#F56C6C'
},
b: {
color: '#303133'
}
}
},
lineStyle: {
curveness: 0.08,
opacity: 1,
width: 2
},
value: {
type: '3',
des: callStr
}
}
},
drawEchart() {
this.echartOptions = {
title: {
text: '链路跟踪'
},
tooltip: this.getToolTip(),
animationDurationUpdate: 100,
animationEasingUpdate: 'quinticInOut',
series: [
{
type: 'graph',
layout: 'none',
symbolSize: 50,
roam: true,
label: {
show: true
},
edgeSymbol: ['circle', 'arrow'],
edgeSymbolSize: [4, 10],
edgeLabel: {
fontSize: 15
},
data: this.nodeData,
links: this.linksData,
lineStyle: {
opacty: 0.9,
width: 2,
curveness: 0.2
}
}
]
}
this.graphEchart.setOption(this.echartOptions, true)
}
}
}
</script>
<style lang="scss" scoped>
#graphEchart {
width: 100%;
height: calc(100% - 40px);
}
</style>
// 使用
<flow ref="flow" />
// 获取数据,调用initEchart 方法
this.$refs.flow.initEchart(data)