下面是用到的组件和数据格式
Flow.vue
<template lang="pug">
.flow
.header
div(v-for="(item, index) in ipArr" :key="index")
ip-flow(:data="item")
.common-content
div.end-data
div.flow-content(:style="{width: getStyleWidth(true, ipArrIndex)}" v-for="(ipArrData, ipArrIndex) in endData" :key="ipArrIndex")
div.common-content-a(:style="{width: getStyleWidth(ipArrIndex === 0 ? false : true, ipArrIndex)}" v-if="item.show" v-for="(item, index) in ipArrData.tableData" :key="index" @click="showDetails(item)")
div(:class="item.leftShow ? (getLeftPosition(item, ipArrIndex) ? 'left' : 'left-2') : (getLeftPosition(item, ipArrIndex) ? 'left-m' : 'left-m-2') ")
span.port {{ item.srcPort }}
div(:class="ipArr.length < 3 ? 'content' : (getEndData(ipArrIndex) ? 'content-m bdrborder' : 'content-m-nowidth')" :style="{width: getStyleWidth((ipArrIndex === 0) ? false : true)}")
div(:class="getPosition(item) ? 'method-text-left' : 'method-text-right'") {{ item.method }}
div(:title="item.ruriUserText" :class="getPosition(item) ? 'ruri-user-text-left' : 'ruri-user-text-right'" :style="{width: getEndData(ipArrIndex) ? '' : getStyleWidth(ipArrIndex === 0 ? false : true)}") {{ item.ruriUserText }}
Arrow(:arrowDirection="getPosition(item) ? 'left' : 'right'" :width="getEndData(ipArrIndex) ? '' : getStyleWidth(ipArrIndex === 0 ? false : true)")
div(:class="getPosition(item) ? 'c-r' : ''")
span.id-type-time [{{ item.id }}] [{{ item.communicationMethod }}] {{ item.createDateText }}
br
span.id-type-time +{{ item.diffTime }}
div(:class="ipArr.length < 3 ? (!getPosition(item) ? 'right' : 'right-2') : (!getPosition(item) ? 'right-m' : 'right-m-2')")
span.d-p {{ item.dstPort }}
div.common-content-b(v-else)
.left(v-if="item.leftShow")
div(:class="getEndData(ipArrIndex) ? 'content-m bdrborder' : 'content-m-nowidth'" :style="{width: getStyleWidth(ipArrIndex === 0 ? false : true)}")
.right-m
</template>
<script>
import Arrow from './Arrow.vue'
import IpFlow from './IpFlow.vue'
export default {
name: 'Flow',
components: {
Arrow,
IpFlow
},
props: {
data: {
type: Object,
default: () => {
return null
}
}
},
data () {
return {
tableData: [],
ipArr: [],
endData: [],
flowContentWidth: ''
}
},
watch: {
data: {
handler: function (val, oldVal) {
if (val && val !== oldVal) {
this.init(val)
}
}
}
},
mounted () {
window.onresize = () => {
this.getWidth()
}
},
methods: {
init (data) {
this.tableData = data.list || []
this.ipArr = data.ipArr || {}
this.ipArr = this.getIpArr(JSON.parse(JSON.stringify(this.ipArr)))
const newIpArr = []
this.ipArr.forEach((item, index) => {
if (this.ipArr[index + 1]) {
const newIp = item.ip + '-' + this.ipArr[index + 1].ip
newIpArr.push(newIp)
}
})
this.endData = []
newIpArr.forEach((ele, eleIndex) => {
const data = {
ipArea: ele,
tableData: []
}
this.tableData.forEach((e, i) => {
const item = JSON.parse(JSON.stringify(e))
const flag = this.getAreaContent(item, ele)
if (eleIndex === 0) {
item.leftShow = true
} else {
item.leftShow = false
}
if (flag) {
item.show = true
} else {
item.show = false
}
data.tableData.push(item)
})
this.endData.push(data)
})
this.getWidth()
},
getWidth () {
this.$nextTick(() => {
const clientWidth = document.querySelectorAll('.end-data')[0].clientWidth
if (this.endData.length >= 2) {
this.flowContentWidth = ((clientWidth - 100) / this.endData.length).toFixed(0)
} else {
this.flowContentWidth = (clientWidth / this.endData.length).toFixed(0)
}
})
},
getIpArr (ipData) {
const tempArr = []
const arr = Object.keys(ipData)
if (arr && arr.length > 0) {
arr.forEach((e) => {
const data = ipData[e]
const ip = data['host'][0].split(':')[0]
data.ip = ip
tempArr.push(data)
})
}
if (tempArr.length > 0) {
return tempArr.sort(this.sortBy('position'))
}
return tempArr
},
sortBy (field) {
return (x, y) => {
return x[field] - y[field]
}
},
getCreateDate (date) {
return this.moment(date).format('YYYY-MM-DD HH:mm:ss.SSS')
},
getPosition (item) {
if (item.arrowDirection === 'right') {
return false
} else {
return true
}
},
getLeftPosition (item, index) {
if (item.arrowDirection === 'left') {
return false
} else {
return true
}
},
getAreaContent (data, area) {
const ipArr = area.split('-')
if ((data.srcIp === ipArr[0] && data.dstIp === ipArr[1]) || (data.srcIp === ipArr[1] && data.dstIp === ipArr[0])) {
return true
} else {
return false
}
},
getAreaContentFlag (data, index) {
if (index === 0) {
if (!this.endData[index]) return false
const ipArr = this.endData[index].ipArea.split('-')
if ((data.srcIp === ipArr[0] && data.dstIp === ipArr[1]) || (data.srcIp === ipArr[1] && data.dstIp === ipArr[0])) {
return false
} else {
return true
}
} else {
if (!this.endData[index + 1]) return false
const ipArr = this.endData[index].ipArea.split('-')
if ((data.srcIp === ipArr[0] && data.dstIp === ipArr[1]) || (data.srcIp === ipArr[1] && data.dstIp === ipArr[0])) {
return false
} else {
return true
}
}
},
getEndData (index) {
if (index === (this.endData.length - 1)) {
return true
} else {
return false
}
},
getStyleWidth (flag, index) {
if (index === 0 || index === (this.endData.length - 1)) {
if (this.endData.length >= 2) {
return '100%'
} else {
return '100%'
}
}
return this.flowContentWidth + 'px'
},
showDetails (data) {
this.$emit('showDetails', data)
}
}
}
</script>
<style lang="less" scoped>
@color: rgb(102, 214, 92);
@bgColor: #EFFAEE;
@borderColor: #e9eaec;
@lineHeight: 91px;
@lineHeight2: 91px;
.method-text() {
font-size: 14px;
font-weight: 600;
margin-left: 3px;
}
.ruri-user-text() {
position: relative;
top: 5px;
}
.no-line () {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.hover-content() {
background-color: #a4a4a447;
cursor: pointer;
}
.common-center() {
.method-text-right {
.method-text();
}
.method-text-left {
.method-text();
text-align: end;
color: @color;
margin-right: 3px;
}
.ruri-user-text-left {
.ruri-user-text();
.no-line ();
text-align: end;
margin-right: 3px;
right: 2px;
}
.ruri-user-text-right {
.ruri-user-text();
.no-line ();
left: 3px;
}
.id-type-time {
position: relative;
top: -5px;
left: 3px;
}
}
.flow {
.header {
display: flex;
justify-content: space-between;
}
.common-content {
display: flex;
flex-wrap: wrap;
.common-content-a {
height: 100px;
display: flex;
.content-m-nowidth:hover {
.hover-content();
}
.content-m:hover {
.hover-content();
}
.content:hover {
.hover-content();
}
}
.common-content-b {
width: 100%;
height: 100px;
display: flex;
.left {
text-align: right;
line-height: @lineHeight;
}
.content-m {
}
}
.end-data {
width: 100%;
display: flex;
}
}
.flow-content {
background-color: @bgColor;
width: 100%;
.left {
width: 50px;
text-align: right;
line-height: @lineHeight;
padding-right: 5px;
}
.left-2 {
width: 50px;
text-align: right;
line-height: @lineHeight2;
padding-right: 5px;
}
.left-m {
width: 0;
text-align: right;
line-height: @lineHeight;
position: relative;
.port {
display: block;
position: relative;
left: -31px;
}
}
.left-m-2 {
width: 0;
text-align: right;
line-height: @lineHeight2;
position: relative;
.port {
display: block;
position: relative;
left: -31px;
}
}
.content {
.common-center();
width: calc(100% - 100px);
border-left: 1px solid @borderColor;
border-right: 1px solid @borderColor;
}
.content-m {
.common-center();
width: calc(100% - 50px);
height: 100px;
border-left: 1px solid @borderColor;
}
.content-m-nowidth {
.common-center();
height: 100px;
border-left: 1px solid @borderColor;
}
.right {
width: 50px;
text-align: left;
line-height: @lineHeight;
padding-left: 5px;
}
.right-2 {
width: 50px;
text-align: left;
line-height: @lineHeight2;
padding-left: 5px;
}
.right-m {
width: 0;
text-align: left;
line-height: @lineHeight;
padding-left: 0;
position: relative;
.d-p {
margin-left: 5px;
}
}
.right-m-2 {
width: 0;
text-align: left;
line-height: @lineHeight2;
padding-left: 0;
position: relative;
.d-p {
margin-left: 5px;
}
}
}
.c-r {
text-align: end;
margin-right: 6px;
}
.bdrborder {
border-right: 1px solid @borderColor;
}
}
</style>
IpFlow.vue
<template lang="pug">
.arrow
div.arrow-content
div.content
.left-arrow
.horizontal-line-left
div.content
.horizontal-line-right
.right-arrow
.bottom
span {{ data.ip }}
</template>
<script>
export default {
name: 'IpFlow',
props: {
data: {
type: Object,
default: () => {
return {}
}
}
},
data () {
return {
}
},
methods: {
}
}
</script>
<style lang="less" scoped>
@color: #A3A3A3;
@borderWidth: 10px;
@borderColor: #e1e1e1;
.arrow-common() {
border: 5px solid transparent;
width: 0;
height: 0px;
}
.horizontal-line () {
border-top: 3px solid @color;
width: 14px;
position: relative;
top: 3px;
}
.arrow {
width: 100px;
border-radius: 5px;
background-color: #f9f9f9;
border: 1px dashed @borderColor;
margin: 0 0 1px 0;
.arrow-content {
padding: 16px;
.content {
display: flex;
justify-content: center;
padding: 1px 0;
}
}
.bottom {
text-align: center;
font-size: 12px;
border-top: 1px solid @borderColor;
padding: 3px;
}
.left-arrow {
.arrow-common();
border-right: @borderWidth solid @color;
position: relative;
left: -6px;
}
.horizontal-line-left {
.horizontal-line ();
left: -6px;
}
.right-arrow {
.arrow-common();
border-left: @borderWidth solid @color;
}
.horizontal-line-right {
.horizontal-line ();
}
}
</style>
Arrow.vue
<template lang="pug">
.arrow(:style="{width: width}")
div.left-arrow(v-if="arrowDirection === 'left'" :style="{borderRight: `10px solid ${color}`}")
div.horizontal-line(:style="{borderTop: `2px solid ${color}`}")
div.right-arrow(v-if="arrowDirection === 'right'" :style="{borderLeft: `10px solid ${color}`}")
</template>
<script>
export default {
name: 'Arrow',
props: {
arrowDirection: {
type: String,
default: 'right'
},
color: {
type: String,
default: 'rgb(102, 214, 92)'
},
width: {
type: String,
default: '100%'
}
},
data () {
return {
}
},
mounted () {
this.init()
},
methods: {
init () {
}
}
}
</script>
<style lang="less" scoped>
@color: rgb(102, 214, 92);
@borderWidth: 14px;
.arrow-common() {
border-top: 8px solid transparent;
border-bottom: 8px solid transparent;
position: relative;
top: -2px;
width: 0;
height: 0;
}
.arrow {
display: flex;
.horizontal-line {
width: 100%;
position: relative;
top: 5px;
}
.left-arrow {
.arrow-common();
}
.right-arrow {
.arrow-common();
}
}
</style>
Flow.vue中data的数据格式
{
"ipArr": {
"21.151.96.161:7100": {
"host": [
"21.151.96.161:7100"
],
"position": 0
},
"10.0.8.14:7100": {
"host": [
"10.0.8.14:7100"
],
"position": 1
}
},
"list": [{
"id": 275231,
"sid": "903997704",
"createDate": "2023-06-01 09:20:02",
"raw": "MESSAGE sip:21140000002000000001@10.0.8.14:7100 SIP/2.0\r\nVia: SIP/2.0/UDP 21.151.96.161:7100;rport;branch=z9hG4bK1567269288\r\nFrom: <sip:21140000002000000022@21.151.96.161:7100>;tag=3670656861\r\nTo: <sip:21140000002000000001@10.0.8.14:7100>\r\nCall-ID: 903997704\r\nCSeq: 20 MESSAGE\r\nContent-Type: Application/MANSCDP+xml\r\nMax-Forwards: 70\r\nUser-Agent: NCG V2.3.9.205749\r\nContent-Length: 154\r\n\r\n<?xml version=\"1.0\"?>\n<Notify>\r\n<CmdType>Keepalive</CmdType>\r\n<SN>222170</SN>\r\n<DeviceID>21140000002000000022</DeviceID>\r\n<Status>OK</Status>\r\n</Notify>\r\n",
"dstIp": "10.0.8.14",
"srcIp": "21.151.96.161",
"dstPort": 7100,
"srcPort": 7100,
"protocol": 17,
"captureId": "2002",
"payloadType": 1,
"timeSeconds": 1685582402,
"timeUseconds": 746522,
"correlation_id": "903997704",
"protocolFamily": 2,
"cseq": "20 MESSAGE",
"callid": "903997704",
"method": "MESSAGE",
"to_user": "21140000002000000001",
"from_tag": "3670656861",
"from_user": "21140000002000000022",
"ruri_user": "21140000002000000001",
"user_agent": "NCG V2.3.9.205749",
"ruri_domain": "10.0.8.14",
"createDateText": "2023-06-01 09:20:02.746",
"diffTime": "0ms",
"arrowDirection": "right",
"ruriUserText": "21140000002000000001,MESSAGE sip:21140000002000000001@10.0.8.14:7100 SIP/2.0",
"communicationMethod": "UDP"
},
{
"id": 275236,
"sid": "903997704",
"createDate": "2023-06-01 09:20:02",
"raw": "SIP/2.0 200 OK\r\nVia: SIP/2.0/UDP 21.151.96.161:7100;rport=7100;branch=z9hG4bK1567269288\r\nFrom: <sip:21140000002000000022@21.151.96.161:7100>;tag=3670656861\r\nTo: <sip:21140000002000000001@10.0.8.14:7100>;tag=3511899241\r\nCall-ID: 903997704\r\nCSeq: 20 MESSAGE\r\nUser-Agent: NcgV2.3.6\r\nContent-Length: 0\r\n\r\n",
"dstIp": "21.151.96.161",
"srcIp": "10.0.8.14",
"dstPort": 7100,
"srcPort": 7100,
"protocol": 17,
"captureId": "2002",
"payloadType": 1,
"timeSeconds": 1685582402,
"timeUseconds": 751882,
"correlation_id": "903997704",
"protocolFamily": 2,
"cseq": "20 MESSAGE",
"callid": "903997704",
"method": "200",
"to_user": "21140000002000000001",
"from_tag": "3670656861",
"from_user": "21140000002000000022",
"ruri_user": "",
"user_agent": "NcgV2.3.6",
"ruri_domain": "",
"createDateText": "2023-06-01 09:20:02.751",
"diffTime": "5ms",
"arrowDirection": "left",
"ruriUserText": "SIP/2.0 200 OK\r\nVia: SIP/2.0/UDP 21.151.96.161:7100;",
"communicationMethod": "UDP"
}
]
}