mirror of
https://github.com/crawlab-team/crawlab.git
synced 2026-01-22 17:31:03 +01:00
added network diagram
This commit is contained in:
177
frontend/src/components/Node/NodeNetwork.vue
Normal file
177
frontend/src/components/Node/NodeNetwork.vue
Normal file
@@ -0,0 +1,177 @@
|
||||
<template>
|
||||
<div id="network-chart"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
|
||||
export default {
|
||||
name: 'NodeNetwork',
|
||||
props: {
|
||||
activeTab: {
|
||||
type: String
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
activeTab () {
|
||||
setTimeout(() => {
|
||||
this.render()
|
||||
}, 0)
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
chart: undefined
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
masterNode () {
|
||||
const nodes = this.$store.state.node.nodeList
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
if (nodes[i].is_master) {
|
||||
return nodes[i]
|
||||
}
|
||||
}
|
||||
return {}
|
||||
},
|
||||
nodes () {
|
||||
let nodes = this.$store.state.node.nodeList
|
||||
nodes = nodes.map(d => {
|
||||
d.id = d._id
|
||||
d.x = Math.floor(100 * Math.random())
|
||||
d.y = Math.floor(100 * Math.random())
|
||||
d.itemStyle = {
|
||||
color: d.is_master ? '#409EFF' : '#e6a23c'
|
||||
}
|
||||
return d
|
||||
})
|
||||
|
||||
// mongodb
|
||||
nodes.push({
|
||||
id: 'mongodb',
|
||||
name: 'MongoDB',
|
||||
x: Math.floor(100 * Math.random()),
|
||||
y: Math.floor(100 * Math.random()),
|
||||
itemStyle: {
|
||||
color: '#67c23a'
|
||||
}
|
||||
})
|
||||
|
||||
// redis
|
||||
nodes.push({
|
||||
id: 'redis',
|
||||
name: 'Redis',
|
||||
x: Math.floor(100 * Math.random()),
|
||||
y: Math.floor(100 * Math.random()),
|
||||
itemStyle: {
|
||||
color: '#f56c6c'
|
||||
}
|
||||
})
|
||||
|
||||
return nodes
|
||||
},
|
||||
links () {
|
||||
const links = []
|
||||
for (let i = 0; i < this.nodes.length; i++) {
|
||||
if (['redis', 'mongodb'].includes(this.nodes[i].id)) continue
|
||||
// mongodb
|
||||
links.push({
|
||||
source: this.nodes[i].id,
|
||||
target: 'mongodb',
|
||||
value: 10,
|
||||
lineStyle: {
|
||||
color: '#67c23a'
|
||||
}
|
||||
})
|
||||
|
||||
// redis
|
||||
links.push({
|
||||
source: this.nodes[i].id,
|
||||
target: 'redis',
|
||||
value: 10,
|
||||
lineStyle: {
|
||||
color: '#f56c6c'
|
||||
}
|
||||
})
|
||||
|
||||
if (this.masterNode.id === this.nodes[i].id) continue
|
||||
|
||||
// master
|
||||
links.push({
|
||||
source: this.masterNode.id,
|
||||
target: this.nodes[i].id,
|
||||
value: 0.5,
|
||||
lineStyle: {
|
||||
color: '#409EFF'
|
||||
}
|
||||
})
|
||||
}
|
||||
return links
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
render () {
|
||||
const option = {
|
||||
title: {
|
||||
text: this.$t('Node Network')
|
||||
},
|
||||
tooltip: {},
|
||||
animationDurationUpdate: 1500,
|
||||
animationEasingUpdate: 'quinticInOut',
|
||||
series: [
|
||||
{
|
||||
type: 'graph',
|
||||
layout: 'force',
|
||||
symbolSize: 50,
|
||||
roam: true,
|
||||
label: {
|
||||
normal: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
edgeSymbol: ['circle', 'arrow'],
|
||||
edgeSymbolSize: [4, 10],
|
||||
edgeLabel: {
|
||||
normal: {
|
||||
textStyle: {
|
||||
fontSize: 20
|
||||
}
|
||||
}
|
||||
},
|
||||
focusOneNodeAdjacency: true,
|
||||
force: {
|
||||
initLayout: 'force',
|
||||
repulsion: 50,
|
||||
gravity: 0.001,
|
||||
edgeLength: 40
|
||||
},
|
||||
draggable: true,
|
||||
data: this.nodes,
|
||||
links: this.links,
|
||||
lineStyle: {
|
||||
normal: {
|
||||
opacity: 0.9,
|
||||
width: 2,
|
||||
curveness: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
this.chart = echarts.init(this.$el)
|
||||
this.chart.setOption(option)
|
||||
this.chart.resize()
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.render()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
#network-chart {
|
||||
height: 480px;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -78,6 +78,11 @@ export default {
|
||||
'Node Port': '节点端口',
|
||||
'Description': '描述',
|
||||
'All Nodes': '所有节点',
|
||||
'Node List': '节点列表',
|
||||
'Network': '拓扑图',
|
||||
'Node Network': '节点拓扑图',
|
||||
'Master': '主节点',
|
||||
'Worker': '工作节点',
|
||||
|
||||
// 节点列表
|
||||
'IP': 'IP地址',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<!--filter-->
|
||||
<div class="filter">
|
||||
<div v-if="false" class="filter">
|
||||
<el-input prefix-icon="el-icon-search"
|
||||
:placeholder="$t('Search')"
|
||||
class="filter-search"
|
||||
@@ -17,103 +17,122 @@
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<!--./filter-->
|
||||
|
||||
<!--table list-->
|
||||
<el-table :data="filteredTableData"
|
||||
class="table"
|
||||
:header-cell-style="{background:'rgb(48, 65, 86)',color:'white'}"
|
||||
border
|
||||
@expand-change="onRowExpand">
|
||||
<el-table-column type="expand">
|
||||
<template slot-scope="scope">
|
||||
<el-form class="node-detail" :model="scope.row" label-width="120px" inline>
|
||||
<el-form-item :label="$t('OS')">
|
||||
<span>{{scope.row.systemInfo ? getOSName(scope.row.systemInfo.os) : ''}}</span>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('ARCH')">
|
||||
<span>{{scope.row.systemInfo ? scope.row.systemInfo.arch : ''}}</span>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('Number of CPU')">
|
||||
<span>{{scope.row.systemInfo ? scope.row.systemInfo.num_cpu : ''}}</span>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-form class="node-detail executable" :model="scope.row" label-width="120px">
|
||||
<el-form-item :label="$t('Executables')">
|
||||
<ul v-if="true" class="executable-list">
|
||||
<li
|
||||
v-for="(ex, index) in getExecutables(scope.row)"
|
||||
:key="index"
|
||||
class="executable-item"
|
||||
>
|
||||
<el-tooltip :content="ex.path">
|
||||
<div>
|
||||
<template v-if="ex.file_name.match(/^python/)">
|
||||
<font-awesome-icon :icon="['fab','python']"/>
|
||||
</template>
|
||||
<template v-else-if="ex.file_name.match(/^java/)">
|
||||
<font-awesome-icon :icon="['fab','java']"/>
|
||||
</template>
|
||||
<template v-else-if="ex.file_name.match(/^bash$|^sh$/)">
|
||||
<font-awesome-icon :icon="['fab','linux']"/>
|
||||
</template>
|
||||
<template v-else-if="ex.file_name.match(/^node/)">
|
||||
<font-awesome-icon :icon="['fab','node-js']"/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<font-awesome-icon :icon="['fas', 'terminal']"/>
|
||||
</template>
|
||||
<span class="executable-label">{{ex.display_name}}</span>
|
||||
</div>
|
||||
</el-tooltip>
|
||||
</li>
|
||||
</ul>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template v-for="col in columns">
|
||||
<el-table-column v-if="col.name === 'status'"
|
||||
:key="col.name"
|
||||
:label="$t(col.label)"
|
||||
:sortable="col.sortable"
|
||||
:width="col.width">
|
||||
<template slot-scope="scope">
|
||||
<el-tag type="info" v-if="scope.row.status === 'offline'">{{$t('Offline')}}</el-tag>
|
||||
<el-tag type="success" v-else-if="scope.row.status === 'online'">{{$t('Online')}}</el-tag>
|
||||
<el-tag type="danger" v-else>{{$t('Unavailable')}}</el-tag>
|
||||
<el-tabs type="border-card" v-model="activeTab">
|
||||
<el-tab-pane :label="$t('Node List')">
|
||||
<!--table list-->
|
||||
<el-table :data="filteredTableData"
|
||||
class="table"
|
||||
:header-cell-style="{background:'rgb(48, 65, 86)',color:'white'}"
|
||||
border
|
||||
@expand-change="onRowExpand">
|
||||
<el-table-column type="expand">
|
||||
<template slot-scope="scope">
|
||||
<el-form class="node-detail" :model="scope.row" label-width="120px" inline>
|
||||
<el-form-item :label="$t('OS')">
|
||||
<span>{{scope.row.systemInfo ? getOSName(scope.row.systemInfo.os) : ''}}</span>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('ARCH')">
|
||||
<span>{{scope.row.systemInfo ? scope.row.systemInfo.arch : ''}}</span>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('Number of CPU')">
|
||||
<span>{{scope.row.systemInfo ? scope.row.systemInfo.num_cpu : ''}}</span>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-form class="node-detail executable" :model="scope.row" label-width="120px">
|
||||
<el-form-item :label="$t('Executables')">
|
||||
<ul v-if="true" class="executable-list">
|
||||
<li
|
||||
v-for="(ex, index) in getExecutables(scope.row)"
|
||||
:key="index"
|
||||
class="executable-item"
|
||||
>
|
||||
<el-tooltip :content="ex.path">
|
||||
<div>
|
||||
<template v-if="ex.file_name.match(/^python/)">
|
||||
<font-awesome-icon :icon="['fab','python']"/>
|
||||
</template>
|
||||
<template v-else-if="ex.file_name.match(/^java/)">
|
||||
<font-awesome-icon :icon="['fab','java']"/>
|
||||
</template>
|
||||
<template v-else-if="ex.file_name.match(/^bash$|^sh$/)">
|
||||
<font-awesome-icon :icon="['fab','linux']"/>
|
||||
</template>
|
||||
<template v-else-if="ex.file_name.match(/^node/)">
|
||||
<font-awesome-icon :icon="['fab','node-js']"/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<font-awesome-icon :icon="['fas', 'terminal']"/>
|
||||
</template>
|
||||
<span class="executable-label">{{ex.display_name}}</span>
|
||||
</div>
|
||||
</el-tooltip>
|
||||
</li>
|
||||
</ul>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template v-for="col in columns">
|
||||
<el-table-column v-if="col.name === 'status'"
|
||||
:key="col.name"
|
||||
:label="$t(col.label)"
|
||||
:sortable="col.sortable"
|
||||
:width="col.width">
|
||||
<template slot-scope="scope">
|
||||
<el-tag type="info" v-if="scope.row.status === 'offline'">{{$t('Offline')}}</el-tag>
|
||||
<el-tag type="success" v-else-if="scope.row.status === 'online'">{{$t('Online')}}</el-tag>
|
||||
<el-tag type="danger" v-else>{{$t('Unavailable')}}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-else-if="col.name === 'type'"
|
||||
:key="col.name"
|
||||
:label="$t(col.label)"
|
||||
:sortable="col.sortable"
|
||||
:width="col.width">
|
||||
<template slot-scope="scope">
|
||||
<el-tag type="primary" v-if="scope.row.is_master">{{$t('Master')}}</el-tag>
|
||||
<el-tag type="warning" v-else>{{$t('Worker')}}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-else
|
||||
:key="col.name"
|
||||
:property="col.name"
|
||||
:label="$t(col.label)"
|
||||
:sortable="col.sortable"
|
||||
:align="col.align || 'left'"
|
||||
:width="col.width">
|
||||
</el-table-column>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-else
|
||||
:key="col.name"
|
||||
:property="col.name"
|
||||
:label="$t(col.label)"
|
||||
:sortable="col.sortable"
|
||||
:align="col.align || 'left'"
|
||||
:width="col.width">
|
||||
</el-table-column>
|
||||
</template>
|
||||
<el-table-column :label="$t('Action')" align="left" width="160" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-tooltip :content="$t('View')" placement="top">
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="onView(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip :content="$t('Remove')" placement="top">
|
||||
<el-button type="danger" icon="el-icon-delete" size="mini" @click="onRemove(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="pagination">
|
||||
<el-pagination
|
||||
@current-change="onPageChange"
|
||||
@size-change="onPageChange"
|
||||
:current-page.sync="pagination.pageNum"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:page-size.sync="pagination.pageSize"
|
||||
layout="sizes, prev, pager, next"
|
||||
:total="nodeList.length">
|
||||
</el-pagination>
|
||||
</div>
|
||||
<el-table-column :label="$t('Action')" align="left" width="160" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-tooltip :content="$t('View')" placement="top">
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="onView(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip :content="$t('Remove')" placement="top">
|
||||
<el-button type="danger" icon="el-icon-delete" size="mini" @click="onRemove(scope.row)"></el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="pagination">
|
||||
<el-pagination
|
||||
@current-change="onPageChange"
|
||||
@size-change="onPageChange"
|
||||
:current-page.sync="pagination.pageNum"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:page-size.sync="pagination.pageSize"
|
||||
layout="sizes, prev, pager, next"
|
||||
:total="nodeList.length">
|
||||
</el-pagination>
|
||||
</div>
|
||||
<!--./table list-->
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="$t('Network')">
|
||||
<node-network :active-tab="activeTab"/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -121,9 +140,11 @@
|
||||
import {
|
||||
mapState
|
||||
} from 'vuex'
|
||||
import NodeNetwork from '../../components/Node/NodeNetwork'
|
||||
|
||||
export default {
|
||||
name: 'NodeList',
|
||||
components: { NodeNetwork },
|
||||
data () {
|
||||
return {
|
||||
pagination: {
|
||||
@@ -139,13 +160,15 @@ export default {
|
||||
columns: [
|
||||
{ name: 'name', label: 'Name', width: '220' },
|
||||
{ name: 'ip', label: 'IP', width: '160' },
|
||||
{ name: 'type', label: 'Type', width: '120' },
|
||||
// { name: 'port', label: 'Port', width: '80' },
|
||||
{ name: 'status', label: 'Status', width: '120', sortable: true },
|
||||
{ name: 'description', label: 'Description', width: 'auto' }
|
||||
],
|
||||
nodeFormRules: {
|
||||
name: [{ required: true, message: 'Required Field', trigger: 'change' }]
|
||||
}
|
||||
},
|
||||
activeTab: undefined
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
||||
Reference in New Issue
Block a user