新功能(服务监控): 新增服务监控功能;
parent
3a72840301
commit
d7a7e09099
|
@ -41,14 +41,16 @@
|
|||
"axios": "0.21.0",
|
||||
"clipboard": "2.0.6",
|
||||
"core-js": "3.8.1",
|
||||
"echarts": "4.9.0",
|
||||
"echarts": "^4.9.0",
|
||||
"element-ui": "2.15.0",
|
||||
"eslint-loader": "^4.0.2",
|
||||
"file-saver": "2.0.4",
|
||||
"fuse.js": "6.4.3",
|
||||
"highlight.js": "9.18.5",
|
||||
"js-beautify": "1.13.0",
|
||||
"js-cookie": "2.2.1",
|
||||
"jsencrypt": "3.0.0-rc.1",
|
||||
"lodash": "^4.17.21",
|
||||
"moment": "^2.29.1",
|
||||
"nprogress": "0.2.0",
|
||||
"quill": "1.3.7",
|
||||
|
@ -58,6 +60,7 @@
|
|||
"vue-count-to": "1.0.13",
|
||||
"vue-cropper": "0.5.5",
|
||||
"vue-router": "3.4.9",
|
||||
"vue-types": "^2.0.3",
|
||||
"vuedraggable": "2.24.3",
|
||||
"vuex": "3.6.0"
|
||||
},
|
||||
|
@ -65,6 +68,7 @@
|
|||
"@vue/cli-plugin-babel": "4.4.6",
|
||||
"@vue/cli-plugin-eslint": "4.4.6",
|
||||
"@vue/cli-service": "4.4.6",
|
||||
"@vue/composition-api": "^1.0.0-rc.6",
|
||||
"babel-eslint": "10.1.0",
|
||||
"chalk": "4.1.0",
|
||||
"connect": "3.6.6",
|
||||
|
|
|
@ -3,7 +3,7 @@ import request from '@/utils/request'
|
|||
// 获取路由
|
||||
export const getRouters = () => {
|
||||
return request({
|
||||
url: '/admin/getRouters',
|
||||
url: '/admin/getRouters/',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,9 +1,65 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
// 查询服务器详细
|
||||
export function getServer() {
|
||||
// 查询服务器信息详细
|
||||
export function getServerList(params) {
|
||||
return request({
|
||||
url: '/monitor/server',
|
||||
url: 'admin/monitor/server/',
|
||||
params,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 修改服务器信息
|
||||
export function updateServerInfo(id, data) {
|
||||
let {name, remark} = data;
|
||||
return request({
|
||||
url: `admin/monitor/server/${id}/`,
|
||||
data: {
|
||||
name,
|
||||
remark
|
||||
},
|
||||
method: 'PUT'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取监控配置信息
|
||||
export function getMonitorStatusInfo() {
|
||||
return request({
|
||||
url: 'admin/monitor/monitor/enabled/',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 更新监控配置信息
|
||||
export function updateMonitorStatusInfo(params) {
|
||||
return request({
|
||||
url: 'admin/monitor/monitor/enabled/',
|
||||
params,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 清空记录
|
||||
export function cleanMonitorLog() {
|
||||
return request({
|
||||
url: 'admin/monitor/monitor/clean/',
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取监控记录
|
||||
export function getMonitorLogs(id, params) {
|
||||
return request({
|
||||
url: `admin/monitor/monitor/${id}/`,
|
||||
params,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 获取服务器最新监控日志信息
|
||||
export function getServerLatestLog(id) {
|
||||
return request({
|
||||
url: `admin/monitor/monitor/info/${id}/`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
|
|
@ -24,11 +24,28 @@ const permission = {
|
|||
return new Promise(resolve => {
|
||||
// 向后端请求路由数据
|
||||
getRouters().then(res => {
|
||||
const data = handleTree(res.data, "id");
|
||||
let tempData = handleTree(res.data, "id");
|
||||
tempData[2].children.push({
|
||||
component: "vadmin/monitor/server/index",
|
||||
hidden: false,
|
||||
id: 97,
|
||||
meta: {title: "服务监控", icon: "server", noCache: false},
|
||||
name: "server",
|
||||
orderNum: 3,
|
||||
parentId: 66,
|
||||
path: "server",
|
||||
redirect: "server"
|
||||
})
|
||||
const data = tempData
|
||||
|
||||
|
||||
// console.log("handleTree:", data)
|
||||
const sdata = JSON.parse(JSON.stringify(data))
|
||||
const rdata = JSON.parse(JSON.stringify(data))
|
||||
const sidebarRoutes = filterAsyncRouter(sdata)
|
||||
// console.log(sidebarRoutes)
|
||||
const rewriteRoutes = filterAsyncRouter(rdata, false, true)
|
||||
// console.log(rewriteRoutes)
|
||||
rewriteRoutes.push({ path: '*', redirect: '/404', hidden: true })
|
||||
commit('SET_ROUTES', rewriteRoutes)
|
||||
commit('SET_SIDEBAR_ROUTERS', sidebarRoutes)
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
<template>
|
||||
<div class="instrument-board">
|
||||
<div v-if="topTitle.show" class="instrument-board-title">
|
||||
{{ topTitleKeyToNameMapping[topTitle.text] || topTitle.text }}
|
||||
</div>
|
||||
<div :id="ringGraphId" class="instrument-board-body"></div>
|
||||
<div v-if="subTitle.show" class="instrument-board-subtitle">{{ subTitleContent }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import VueTypes from 'vue-types'
|
||||
// 引入基本模板,按需加载
|
||||
const echarts = require('echarts/lib/echarts')
|
||||
require('echarts/lib/chart/gauge')
|
||||
|
||||
export default {
|
||||
name: 'InstrumentBoard',
|
||||
props: {
|
||||
// 组件唯一id
|
||||
ringGraphId: VueTypes.string.isRequired,
|
||||
// 上标题
|
||||
topTitle: VueTypes.shape({
|
||||
show: VueTypes.bool,
|
||||
text: VueTypes.string
|
||||
}).def({
|
||||
show: false
|
||||
}),
|
||||
// 下标题
|
||||
subTitle: VueTypes.shape({
|
||||
show: VueTypes.bool,
|
||||
total: VueTypes.any,
|
||||
used: VueTypes.any,
|
||||
unit: VueTypes.string
|
||||
}).def({
|
||||
show: false
|
||||
}),
|
||||
// 使用率-数值
|
||||
usingRate: VueTypes.number.isRequired,
|
||||
// 使用率样式配置
|
||||
usingRateStyle: VueTypes.object.def({
|
||||
color: '#28BCFE',
|
||||
fontSize: 18,
|
||||
itemColor: ['#25BFFF', '#5284DE', '#2A95F9']
|
||||
}),
|
||||
topTitleKeyToNameMapping: VueTypes.object.def({
|
||||
cpu: 'CPU使用率',
|
||||
memory: '内存使用率',
|
||||
disk: '磁盘使用率'
|
||||
})
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
computed: {
|
||||
subTitleContent() {
|
||||
let used = this.subTitle.used ? this.subTitle.used + '/' : ''
|
||||
let total = this.subTitle.total ? this.subTitle.total : ''
|
||||
let unit = this.subTitle.unit ? ` (${this.subTitle.unit})` : ''
|
||||
return `${used}${total}${unit} `
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.drawBar()
|
||||
},
|
||||
methods: {
|
||||
drawBar() {
|
||||
let currentRate = [this.usingRate]
|
||||
// 基于dom,初始化echarts实例
|
||||
let RingGraph = echarts.init(document.getElementById(this.ringGraphId))
|
||||
|
||||
let option = {
|
||||
title: {
|
||||
text: currentRate + '%',
|
||||
textStyle: this.usingRateStyle,
|
||||
itemGap: 10,
|
||||
left: 'center',
|
||||
top: '45%'
|
||||
},
|
||||
angleAxis: {
|
||||
max: 100,
|
||||
clockwise: true, // 逆时针
|
||||
// 隐藏刻度线
|
||||
show: false
|
||||
},
|
||||
radiusAxis: {
|
||||
type: 'category',
|
||||
show: true,
|
||||
axisLabel: {
|
||||
show: false
|
||||
},
|
||||
axisLine: {
|
||||
show: false
|
||||
},
|
||||
axisTick: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
polar: {
|
||||
center: ['50%', '50%'], // 坐标中心位置
|
||||
radius: '100%' //图形大小
|
||||
},
|
||||
series: [{
|
||||
type: 'bar',
|
||||
data: currentRate,
|
||||
showBackground: true,
|
||||
backgroundStyle: {
|
||||
color: '#BDEBFF' // 底圈颜色
|
||||
},
|
||||
coordinateSystem: 'polar',
|
||||
roundCap: true,
|
||||
barWidth: 15,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
opacity: 1,
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
|
||||
offset: 0,
|
||||
color: this.usingRateStyle.itemColor[0] || '#25BFFF'
|
||||
}, {
|
||||
offset: 1,
|
||||
color: this.usingRateStyle.itemColor[1] || '#5284DE'
|
||||
}]),
|
||||
shadowBlur: 1,
|
||||
shadowColor: this.usingRateStyle.itemColor[2] || '#2A95F9'
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
// 绘制图表
|
||||
RingGraph.setOption(option)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.instrument-board-title {
|
||||
font-weight: bolder;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.instrument-board-body {
|
||||
min-height: 200px;
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.instrument-board-subtitle {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,183 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="line-chart-title">
|
||||
<div class="line-chart-name">{{ chartTitle }}</div>
|
||||
<div class="line-chart-time-radio">
|
||||
<el-radio-group v-model="timeLimit" @change="changeTimeLimit">
|
||||
<el-radio-button
|
||||
v-for="item in Object.keys(TIME_LIMIT_SETTING)"
|
||||
:label="TIME_LIMIT_SETTING[item].name"
|
||||
:key="TIME_LIMIT_SETTING[item].key"
|
||||
></el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
<div :id="lineChartId" class="line-chart-body"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import VueTypes from 'vue-types'
|
||||
// 引入基本模板,按需加载
|
||||
import echarts from 'echarts'
|
||||
import moment from 'moment'
|
||||
import { getMonitorLogs } from '@/api/vadmin/monitor/server'
|
||||
|
||||
const MONTH = moment().month()
|
||||
const YEAR = moment().year()
|
||||
const TODAY = moment().format('YYYY-MM-DD')
|
||||
const YESTERDAY = moment().subtract(7, 'days').format('YYYY-MM-DD')
|
||||
const LAST_SEVEN_DAYS = moment().subtract(7, 'days').format('YYYY-MM-DD')
|
||||
const LAST_THIRTY_DAYS = moment().subtract(30, 'days').format('YYYY-MM-DD')
|
||||
|
||||
// 时间段筛选配置
|
||||
const TIME_LIMIT_SETTING = {
|
||||
'yesterday': {
|
||||
key: 'yesterday',
|
||||
name: '昨天',
|
||||
timeRange: [
|
||||
`${YESTERDAY} 00:00:00`,
|
||||
`${YESTERDAY} 23:59:59`
|
||||
]
|
||||
},
|
||||
'today': {
|
||||
key: 'today',
|
||||
name: '今天',
|
||||
timeRange: [
|
||||
`${TODAY} 00:00:00`,
|
||||
`${TODAY} 23:59:59`
|
||||
]
|
||||
},
|
||||
'latestWeek': {
|
||||
key: 'latestWeek',
|
||||
name: '最近7天',
|
||||
timeRange: [
|
||||
`${LAST_SEVEN_DAYS} 00:00:00`,
|
||||
`${TODAY} 23:59:59`
|
||||
]
|
||||
},
|
||||
'latestMonth': {
|
||||
key: 'latestMonth',
|
||||
name: '最近30天',
|
||||
timeRange: [
|
||||
`${LAST_THIRTY_DAYS} 00:00:00`,
|
||||
`${TODAY} 23:59:59`
|
||||
]
|
||||
}
|
||||
}
|
||||
// 默认显示时间
|
||||
const DEFAULT_TIME = '今天'
|
||||
|
||||
export default {
|
||||
name: 'LineChart',
|
||||
props: {
|
||||
serverInfo: VueTypes.object.isRequired,
|
||||
lineChartKey: VueTypes.string.isRequired,
|
||||
chartTitle: VueTypes.string.isRequired,
|
||||
chartData: VueTypes.array.isRequired
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
TIME_LIMIT_SETTING,
|
||||
timeLimit: DEFAULT_TIME,
|
||||
lineChartId: this.lineChartKey + 'Chart',
|
||||
lineChartData: this.chartData
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.drawBar()
|
||||
},
|
||||
computed: {
|
||||
timeLimitNames() {
|
||||
return Object.keys(TIME_LIMIT_SETTING).map(item => {
|
||||
return TIME_LIMIT_SETTING[item].name
|
||||
})
|
||||
},
|
||||
currentDateIndex() {
|
||||
return this.timeLimitNames.indexOf(this.timeLimit)
|
||||
},
|
||||
currentTimeLimitSetting() {
|
||||
return TIME_LIMIT_SETTING[Object.keys(TIME_LIMIT_SETTING)[this.currentDateIndex]]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
drawBar() {
|
||||
// 基于dom,初始化echarts实例
|
||||
let barGraph = echarts.init(document.getElementById(this.lineChartId))
|
||||
// 绘制图表
|
||||
barGraph.setOption({
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{a} <br/>{b} : {c}'
|
||||
},
|
||||
legend: {
|
||||
left: 'center',
|
||||
data: [],
|
||||
bottom: 0
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
name: 'x',
|
||||
splitLine: { show: false },
|
||||
data: []
|
||||
},
|
||||
grid: {
|
||||
left: '1%',
|
||||
right: '2%',
|
||||
bottom: '8%',
|
||||
containLabel: true
|
||||
},
|
||||
yAxis: {
|
||||
type: 'category',
|
||||
name: '百分比',
|
||||
splitLine: { show: true },
|
||||
data: ['10%', '20%', '30%', '40%', '50%', '60%', '70%', '80%', '90%', '100%']
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '使用率',
|
||||
type: 'line',
|
||||
data: this.lineChartData
|
||||
}
|
||||
]
|
||||
})
|
||||
},
|
||||
changeTimeLimit(value) {
|
||||
this.timeLimit = value
|
||||
this.getCurrentServerMonitorLogs()
|
||||
},
|
||||
getCurrentServerMonitorLogs() {
|
||||
getMonitorLogs(this.serverInfo.id,{ as: { 'create_datetime__range': this.currentTimeLimitSetting.timeRange } }).then(results => {
|
||||
this.lineChartData = results.data[this.lineChartKey]
|
||||
this.drawBar()
|
||||
}).catch(error => {
|
||||
this.$message.warning(error.msg || `获取${this.chartTitle}数据失败!`)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.line-chart-name {
|
||||
font-weight: bolder;
|
||||
width: 20%;
|
||||
min-width: 30px;
|
||||
text-align: left;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.line-chart-time-radio {
|
||||
font-weight: bolder;
|
||||
width: 80%;
|
||||
min-width: 200px;
|
||||
text-align: right;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.line-chart-body {
|
||||
min-height: 300px;
|
||||
min-width: 300px;
|
||||
}
|
||||
</style>
|
|
@ -1,210 +1,521 @@
|
|||
<template>
|
||||
<div class="app-container">
|
||||
<el-row>
|
||||
<el-col :span="12" class="card-box">
|
||||
<el-card>
|
||||
<div slot="header"><span>CPU</span></div>
|
||||
<div class="el-table el-table--enable-row-hover el-table--medium">
|
||||
<table cellspacing="0" style="width: 100%;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="is-leaf"><div class="cell">属性</div></th>
|
||||
<th class="is-leaf"><div class="cell">值</div></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><div class="cell">核心数</div></td>
|
||||
<td><div class="cell" v-if="server.cpu">{{ server.cpu.cpuNum }}</div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><div class="cell">用户使用率</div></td>
|
||||
<td><div class="cell" v-if="server.cpu">{{ server.cpu.used }}%</div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><div class="cell">系统使用率</div></td>
|
||||
<td><div class="cell" v-if="server.cpu">{{ server.cpu.sys }}%</div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><div class="cell">当前空闲率</div></td>
|
||||
<td><div class="cell" v-if="server.cpu">{{ server.cpu.free }}%</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<!-- 监控控制 -->
|
||||
<div class="server-monitor-control">
|
||||
<!-- 监控启用开关 -->
|
||||
<div class="control-server-monitor same-block">
|
||||
开启监控:
|
||||
<el-switch
|
||||
v-model="isOpeningMonitor"
|
||||
active-color="#13ce66"
|
||||
inactive-color="#ff4949"
|
||||
@change="changeMonitorStatus"
|
||||
>
|
||||
</el-switch>
|
||||
</div>
|
||||
<!-- 更新频率设置 -->
|
||||
<div class="monitor-update-interval same-block">
|
||||
更新频率:
|
||||
<el-input-number v-model="monitorUpdateInterval"
|
||||
label=""
|
||||
class="monitor-update-interval-blank"
|
||||
controls-position="right"
|
||||
:min="minMonitorUpdateInterval"
|
||||
@input="handleIntervalChange"
|
||||
|
||||
<el-col :span="12" class="card-box">
|
||||
<el-card>
|
||||
<div slot="header"><span>内存</span></div>
|
||||
<div class="el-table el-table--enable-row-hover el-table--medium">
|
||||
<table cellspacing="0" style="width: 100%;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="is-leaf"><div class="cell">属性</div></th>
|
||||
<th class="is-leaf"><div class="cell">内存</div></th>
|
||||
<th class="is-leaf"><div class="cell">JVM</div></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><div class="cell">总内存</div></td>
|
||||
<td><div class="cell" v-if="server.mem">{{ server.mem.total }}G</div></td>
|
||||
<td><div class="cell" v-if="server.jvm">{{ server.jvm.total }}M</div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><div class="cell">已用内存</div></td>
|
||||
<td><div class="cell" v-if="server.mem">{{ server.mem.used}}G</div></td>
|
||||
<td><div class="cell" v-if="server.jvm">{{ server.jvm.used}}M</div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><div class="cell">剩余内存</div></td>
|
||||
<td><div class="cell" v-if="server.mem">{{ server.mem.free }}G</div></td>
|
||||
<td><div class="cell" v-if="server.jvm">{{ server.jvm.free }}M</div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><div class="cell">使用率</div></td>
|
||||
<td><div class="cell" v-if="server.mem" :class="{'text-danger': server.mem.usage > 80}">{{ server.mem.usage }}%</div></td>
|
||||
<td><div class="cell" v-if="server.jvm" :class="{'text-danger': server.jvm.usage > 80}">{{ server.jvm.usage }}%</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
></el-input-number>
|
||||
<el-select v-model="intervalType"
|
||||
class="monitor-update-interval-unit"
|
||||
@change="selectIntervalType"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in Object.keys(INTERVAL_ID_TO_TYPE_MAPPING)"
|
||||
:key="INTERVAL_ID_TO_TYPE_MAPPING[item].type"
|
||||
:label="INTERVAL_ID_TO_TYPE_MAPPING[item].name"
|
||||
:value="INTERVAL_ID_TO_TYPE_MAPPING[item].name">
|
||||
</el-option>
|
||||
</el-select>
|
||||
|
||||
<el-col :span="24" class="card-box">
|
||||
<el-card>
|
||||
<div slot="header">
|
||||
<span>服务器信息</span>
|
||||
</div>
|
||||
<!-- 监控日志保存时间 -->
|
||||
<div class="monitor-log-save-time same-block">
|
||||
保存天数:
|
||||
<el-input v-model="monitorLogSavingDays" class=" same-block" style="width: 120px;"></el-input>
|
||||
<el-button type="primary"
|
||||
class="same-block"
|
||||
@click="updateMonitorStatusSettingsInfo"
|
||||
>更改
|
||||
</el-button>
|
||||
</div>
|
||||
<!-- 清空记录 -->
|
||||
<div class="clean-monitor-log same-block">
|
||||
<el-button class="same-block" @click="cleanMonitorLogsInfo" type="warning">清空记录</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="server-monitor-top">
|
||||
<!-- 左侧服务器信息 -->
|
||||
<el-card class="box-card server-information">
|
||||
<div slot="header" class="clearfix">
|
||||
<div class="server-info-item">服务器</div>
|
||||
<el-select filterable
|
||||
v-model="currentServerName"
|
||||
class="server-info-item"
|
||||
placeholder="请选择服务器"
|
||||
@change="chooseServerInfo"
|
||||
>
|
||||
<el-option
|
||||
v-for="(item,index) in allServerInfo"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="index"
|
||||
>
|
||||
</el-option>
|
||||
</el-select>
|
||||
<el-button type="primary" class="server-info-item" @click="updateServerInfo">更改</el-button>
|
||||
</div>
|
||||
<div class="server-info-detail">
|
||||
<div v-for="(key,index) in currentServerInfoKeys" :key="index" class="server-info-detail-line text item">
|
||||
<div class="server-info-detail-item">
|
||||
<div style="width: 30%;display: inline-block;">{{ SERVER_KEY_TO_NAME_MAPPING[key] }}:</div>
|
||||
<div v-if="CHANGEABLE_SERVER_FIELDS.indexOf(key) > -1" style="display: inline-block;">
|
||||
<el-input style="display: inline-block; width: 90%;" v-model="currentServer[key]"></el-input>
|
||||
</div>
|
||||
<div v-else style="display: inline-block; "> {{ currentServer[key] }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="el-table el-table--enable-row-hover el-table--medium">
|
||||
<table cellspacing="0" style="width: 100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><div class="cell">服务器名称</div></td>
|
||||
<td><div class="cell" v-if="server.sys">{{ server.sys.computerName }}</div></td>
|
||||
<td><div class="cell">操作系统</div></td>
|
||||
<td><div class="cell" v-if="server.sys">{{ server.sys.osName }}</div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><div class="cell">服务器IP</div></td>
|
||||
<td><div class="cell" v-if="server.sys">{{ server.sys.computerIp }}</div></td>
|
||||
<td><div class="cell">系统架构</div></td>
|
||||
<td><div class="cell" v-if="server.sys">{{ server.sys.osArch }}</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<el-col :span="24" class="card-box">
|
||||
<el-card>
|
||||
<div slot="header">
|
||||
<span>Java虚拟机信息</span>
|
||||
</div>
|
||||
<div class="el-table el-table--enable-row-hover el-table--medium">
|
||||
<table cellspacing="0" style="width: 100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><div class="cell">Java名称</div></td>
|
||||
<td><div class="cell" v-if="server.jvm">{{ server.jvm.name }}</div></td>
|
||||
<td><div class="cell">Java版本</div></td>
|
||||
<td><div class="cell" v-if="server.jvm">{{ server.jvm.version }}</div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><div class="cell">启动时间</div></td>
|
||||
<td><div class="cell" v-if="server.jvm">{{ server.jvm.startTime }}</div></td>
|
||||
<td><div class="cell">运行时长</div></td>
|
||||
<td><div class="cell" v-if="server.jvm">{{ server.jvm.runTime }}</div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1"><div class="cell">安装路径</div></td>
|
||||
<td colspan="3"><div class="cell" v-if="server.jvm">{{ server.jvm.home }}</div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="1"><div class="cell">项目路径</div></td>
|
||||
<td colspan="3"><div class="cell" v-if="server.sys">{{ server.sys.userDir }}</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="24" class="card-box">
|
||||
<el-card>
|
||||
<div slot="header">
|
||||
<span>磁盘状态</span>
|
||||
</div>
|
||||
<div class="el-table el-table--enable-row-hover el-table--medium">
|
||||
<table cellspacing="0" style="width: 100%;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="is-leaf"><div class="cell">盘符路径</div></th>
|
||||
<th class="is-leaf"><div class="cell">文件系统</div></th>
|
||||
<th class="is-leaf"><div class="cell">盘符类型</div></th>
|
||||
<th class="is-leaf"><div class="cell">总大小</div></th>
|
||||
<th class="is-leaf"><div class="cell">可用大小</div></th>
|
||||
<th class="is-leaf"><div class="cell">已用大小</div></th>
|
||||
<th class="is-leaf"><div class="cell">已用百分比</div></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody v-if="server.sysFiles">
|
||||
<tr v-for="sysFile in server.sysFiles">
|
||||
<td><div class="cell">{{ sysFile.dirName }}</div></td>
|
||||
<td><div class="cell">{{ sysFile.sysTypeName }}</div></td>
|
||||
<td><div class="cell">{{ sysFile.typeName }}</div></td>
|
||||
<td><div class="cell">{{ sysFile.total }}</div></td>
|
||||
<td><div class="cell">{{ sysFile.free }}</div></td>
|
||||
<td><div class="cell">{{ sysFile.used }}</div></td>
|
||||
<td><div class="cell" :class="{'text-danger': sysFile.usage > 80}">{{ sysFile.usage }}%</div></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!-- 右侧仪表盘 -->
|
||||
<el-card class="box-card information-instrument-panel" v-for="(key, index) of Object.keys(instrumentBoardData)"
|
||||
:key="`${index}-${key}`">
|
||||
<instrument-board
|
||||
:top-title="{show:true, text: key}"
|
||||
:ring-graph-id="`${key}UsingRate`"
|
||||
:using-rate="instrumentBoardData[key].rate"
|
||||
:top-title-key-to-name-mapping="INSTRUMENT_BOARD_KEY_TO_NAME_MAPPING"
|
||||
:sub-title="{show:true, used: instrumentBoardData[key].used, total:instrumentBoardData[key].total, unit: instrumentBoardData[key].unit}"
|
||||
:using-rate-style="{...getCircleColor(instrumentBoardData[key].rate)}"
|
||||
></instrument-board>
|
||||
</el-card>
|
||||
</div>
|
||||
<!-- 下方折线图 -->
|
||||
<div class="server-monitor-bottom">
|
||||
<!-- 折线图 -->
|
||||
<el-card class="box-card server-monitor-line-chart" v-for="(key, index) in Object.keys(lineChartData)"
|
||||
:key="`${index}-${key}`">
|
||||
<line-chart :line-chart-key="key"
|
||||
:server-info="currentServer"
|
||||
:chart-title="CHART_KEY_NAME_MAPPING[key]"
|
||||
:chart-data="lineChartData[key]"
|
||||
></line-chart>
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getServer } from "@/api/vadmin/monitor/server";
|
||||
import {
|
||||
cleanMonitorLog,
|
||||
getMonitorLogs,
|
||||
getMonitorStatusInfo,
|
||||
getServerLatestLog,
|
||||
getServerList,
|
||||
updateMonitorStatusInfo,
|
||||
updateServerInfo
|
||||
} from '@/api/vadmin/monitor/server'
|
||||
import InstrumentBoard from '@/views/vadmin/monitor/server/components/InstrumentBoard'
|
||||
import LineChart from '@/views/vadmin/monitor/server/components/LineChart'
|
||||
import moment from 'moment'
|
||||
|
||||
const debounce = require('lodash/debounce')
|
||||
|
||||
// 要展示的信息,key -> name
|
||||
const SERVER_KEY_TO_NAME_MAPPING = {
|
||||
ip: '服务器IP',
|
||||
name: '服务器名称',
|
||||
os: '操作系统',
|
||||
remark: '备注'
|
||||
}
|
||||
|
||||
// 更新频率类型映射
|
||||
const INTERVAL_ID_TO_TYPE_MAPPING = {
|
||||
0: {
|
||||
type: 0,
|
||||
name: '秒',
|
||||
key: 'seconds',
|
||||
second: 1
|
||||
},
|
||||
1: {
|
||||
type: 1,
|
||||
name: '分钟',
|
||||
key: 'minutes',
|
||||
second: 60
|
||||
},
|
||||
2: {
|
||||
type: 2,
|
||||
name: '小时',
|
||||
key: 'hours',
|
||||
second: 60 * 60
|
||||
},
|
||||
3: {
|
||||
type: 3,
|
||||
name: '天',
|
||||
key: 'days',
|
||||
second: 24 * 60 * 60
|
||||
}
|
||||
}
|
||||
const defaultUpdateInterval = INTERVAL_ID_TO_TYPE_MAPPING['0']
|
||||
|
||||
// 图表字段映射
|
||||
const CHART_KEY_NAME_MAPPING = {
|
||||
cpu: 'CPU',
|
||||
memory: '内存',
|
||||
disk: '磁盘'
|
||||
}
|
||||
|
||||
// 仪表盘字段映射
|
||||
const INSTRUMENT_BOARD_KEY_TO_NAME_MAPPING = {
|
||||
cpu: "CPU使用率",
|
||||
memory: "内存使用率",
|
||||
disk: "磁盘使用率"
|
||||
}
|
||||
|
||||
// 仪表盘颜色范围
|
||||
const NORMAL_COLOR = {
|
||||
color: '#28BCFE',
|
||||
itemColor: ['#25bfff', '#5284de', '#2a95f9']
|
||||
}
|
||||
const WARNING_COLOR = {
|
||||
color: '#e6a23c',
|
||||
itemColor: ['#e6a23c', '#cc8b1d', '#ffaf18']
|
||||
}
|
||||
const DANGER_COLOR = {
|
||||
color: '#F56C6C',
|
||||
itemColor: ['#fd666d', '#cf1717', '#b31212']
|
||||
}
|
||||
|
||||
// 服务器信息可修改字段
|
||||
const CHANGEABLE_SERVER_FIELDS = ['name', 'remark']
|
||||
|
||||
export default {
|
||||
name: "Server",
|
||||
name: 'Server',
|
||||
components: {
|
||||
InstrumentBoard,
|
||||
LineChart
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
SERVER_KEY_TO_NAME_MAPPING,
|
||||
INTERVAL_ID_TO_TYPE_MAPPING,
|
||||
CHART_KEY_NAME_MAPPING,
|
||||
CHANGEABLE_SERVER_FIELDS,
|
||||
INSTRUMENT_BOARD_KEY_TO_NAME_MAPPING,
|
||||
timeRange: [
|
||||
`${moment().format('YYYY-MM-DD')} 00:00:00`,
|
||||
`${moment().format('YYYY-MM-DD')} 23:59:59`
|
||||
],
|
||||
// 加载层信息
|
||||
loading: [],
|
||||
// 服务器信息
|
||||
server: []
|
||||
};
|
||||
// 所有服务器信息
|
||||
allServerInfo: [],
|
||||
// 当前展示的服务器名称
|
||||
currentServerName: '',
|
||||
// 当前展示的服务器信息
|
||||
currentServer: {},
|
||||
// 当前展示的服务器信息索引,更新服务器信息时用
|
||||
currentServerIndex: 0,
|
||||
// 开启监控控制按钮
|
||||
isOpeningMonitor: false,
|
||||
// 数据更新频率
|
||||
monitorUpdateInterval: 60,
|
||||
// 最小更新频率值
|
||||
minMonitorUpdateInterval: 0,
|
||||
// 更新频率类型
|
||||
intervalType: defaultUpdateInterval.name,
|
||||
// 更新频率单位对应秒
|
||||
intervalTypeUnits: defaultUpdateInterval.second,
|
||||
// 监控日志保存天数
|
||||
monitorLogSavingDays: 30,
|
||||
// 折线图数据
|
||||
lineChartData: {},
|
||||
// 仪表盘数据
|
||||
instrumentBoardData: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
currentServerInfoKeys() {
|
||||
return Object.keys(this.currentServer).filter(key => {
|
||||
if (SERVER_KEY_TO_NAME_MAPPING[key]) {
|
||||
return { [key]: SERVER_KEY_TO_NAME_MAPPING[key] }
|
||||
}
|
||||
})
|
||||
},
|
||||
intervalNameToSecondMapping() {
|
||||
let intervalNameToSecondMapping = {}
|
||||
Object.values(INTERVAL_ID_TO_TYPE_MAPPING).forEach(item => {
|
||||
intervalNameToSecondMapping[item.name] = item.second
|
||||
})
|
||||
return intervalNameToSecondMapping
|
||||
},
|
||||
monitorStatusInfo() {
|
||||
return {
|
||||
enabled: this.isOpeningMonitor ? 1 : 0,
|
||||
save_days: this.monitorLogSavingDays,
|
||||
interval: this.monitorUpdateInterval * this.intervalTypeUnits
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
currentServer(newServerInfo) {
|
||||
if (newServerInfo) {
|
||||
// 更新最新监控信息
|
||||
this.getServerLatestLogInfo(newServerInfo.id)
|
||||
// 获取监控日志信息
|
||||
this.getCurrentServerMonitorLogs()
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getList();
|
||||
this.openLoading();
|
||||
this.openLoading()
|
||||
// 获取所有服务器信息
|
||||
this.getServerList(this.currentServerIndex)
|
||||
// 获取服务器监控频率设置
|
||||
this.getMonitorStatusSettingsInfo()
|
||||
},
|
||||
methods: {
|
||||
/** 查询服务器信息 */
|
||||
getList() {
|
||||
getServer().then(response => {
|
||||
this.server = response.data;
|
||||
this.loading.close();
|
||||
});
|
||||
/** 查询所有服务器基础信息 */
|
||||
getServerList(serverIndex) {
|
||||
getServerList({ pageNum: 'all' }).then(response => {
|
||||
this.allServerInfo = response.data
|
||||
if (this.allServerInfo.length > 0) {
|
||||
this.currentServer = this.allServerInfo[serverIndex || this.currentServerIndex]
|
||||
this.currentServerName = this.currentServer.name
|
||||
}
|
||||
this.loading.close()
|
||||
})
|
||||
},
|
||||
/**修改服务器信息*/
|
||||
updateServerInfo() {
|
||||
updateServerInfo(this.currentServer.id, this.currentServer).then(results => {
|
||||
this.msgSuccess(results.msg || '修改服务器信息成功!')
|
||||
}).catch(error => {
|
||||
this.$message.error(error.msg || '提交修改服务器信息出错!')
|
||||
})
|
||||
},
|
||||
/** 获取服务器最新监控信息 */
|
||||
getServerLatestLogInfo(serverId) {
|
||||
getServerLatestLog(serverId).then(results => {
|
||||
// this.instrumentBoardData = results.data
|
||||
this.instrumentBoardData = {
|
||||
cpu: {
|
||||
total: 2,
|
||||
used: '', // cpu核心 可不传,如指cpu当前主频,该值可以传
|
||||
rate: 12,
|
||||
unit: '核心' // 默认单位 核心
|
||||
},
|
||||
memory: {
|
||||
total: 1024,
|
||||
used: 512,
|
||||
rate: 70,
|
||||
unit: 'MB' // 默认单位 MB
|
||||
},
|
||||
disk: {
|
||||
total: 50,
|
||||
used: 30,
|
||||
rate: 90,
|
||||
unit: 'GB' // 默认单位 GB
|
||||
}
|
||||
}
|
||||
}).catch(error => {
|
||||
this.msgError(error.msg || '获取服务器最新监控信息错误!')
|
||||
})
|
||||
},
|
||||
/** 获取监控日志信息 */
|
||||
getCurrentServerMonitorLogs() {
|
||||
getMonitorLogs(this.currentServer.id, { as: { 'create_datetime__range': this.timeRange } }).then(results => {
|
||||
// this.lineChartData = results.data
|
||||
this.lineChartData = {
|
||||
cpu: [0.5, 0.43, 0.56, 0.89, 0.5, 0.43, 0.56, 0.89, 0.5, 0.43, 0.56, 0.89, 0.5, 0.43, 0.56, 0.89],
|
||||
memory: [0.6, 0.43, 0.56, 0.56, 0.89, 0.5, 0.43, 0.43, 0.56, 0.56, 0.5, 0.43, 0.56, 0.89, 0.5]
|
||||
}
|
||||
}).catch(error => {
|
||||
this.msgError(error.msg || '获取监控日志信息出错误!')
|
||||
})
|
||||
},
|
||||
|
||||
/** 清除监控日志 */
|
||||
cleanMonitorLogsInfo() {
|
||||
cleanMonitorLog().then(results => {
|
||||
this.msgSuccess(results.msg || '清除记录成功!')
|
||||
}).catch(error => {
|
||||
this.$message.warning(error.msg || '清除记录失败,请重试!')
|
||||
})
|
||||
},
|
||||
|
||||
/** 获取监控配置信息 */
|
||||
getMonitorStatusSettingsInfo() {
|
||||
getMonitorStatusInfo().then(results => {
|
||||
let { enabled, interval, save_days } = results
|
||||
this.isOpeningMonitor = enabled
|
||||
this.monitorLogSavingDays = parseInt(save_days || 30)
|
||||
this.formatInterval(parseInt(interval))
|
||||
}).catch(error => {
|
||||
this.msgError(error.msg || '获取服务器监控配置信息出错误!')
|
||||
})
|
||||
},
|
||||
/** 更新监控配置信息 */
|
||||
updateMonitorStatusSettingsInfo() {
|
||||
updateMonitorStatusInfo(this.monitorStatusInfo).then(result => {
|
||||
this.msgSuccess(result.msg || '更新配置成功!')
|
||||
}).catch((error) => {
|
||||
this.msgError(error.msg || '更新服务器监控配置信息出错误!')
|
||||
})
|
||||
},
|
||||
|
||||
// 打开加载层
|
||||
openLoading() {
|
||||
this.loading = this.$loading({
|
||||
lock: true,
|
||||
text: "拼命读取中",
|
||||
spinner: "el-icon-loading",
|
||||
background: "rgba(0, 0, 0, 0.7)"
|
||||
});
|
||||
text: '拼命读取中',
|
||||
spinner: 'el-icon-loading',
|
||||
background: 'rgba(0, 0, 0, 0.7)'
|
||||
})
|
||||
},
|
||||
// 选择展示的服务器信息
|
||||
chooseServerInfo(index) {
|
||||
this.currentServerIndex = index
|
||||
this.currentServer = this.allServerInfo[index]
|
||||
this.currentServerName = this.currentServer.name
|
||||
},
|
||||
// 更改更新频率(周期)数值
|
||||
handleIntervalChange: debounce(function(value) {
|
||||
this.monitorUpdateInterval = value
|
||||
}, 500),
|
||||
// 选择更新频率(周期) 单位
|
||||
selectIntervalType(value) {
|
||||
this.intervalType = value
|
||||
this.intervalTypeUnits = this.intervalNameToSecondMapping[value]
|
||||
},
|
||||
// 修改监控状态
|
||||
changeMonitorStatus(value) {
|
||||
this.isOpeningMonitor = value
|
||||
},
|
||||
// 监控周期时间转换
|
||||
formatInterval(intervalTime) {
|
||||
let biggerInterval = 0
|
||||
for (let interval of Object.values(INTERVAL_ID_TO_TYPE_MAPPING)) {
|
||||
if (interval.second > biggerInterval && interval.second < intervalTime) {
|
||||
biggerInterval = interval.second
|
||||
this.monitorUpdateInterval = intervalTime / interval.second
|
||||
this.intervalType = interval.name
|
||||
this.intervalTypeUnits = interval.second
|
||||
}
|
||||
}
|
||||
},
|
||||
// 仪表盘样式-颜色
|
||||
getCircleColor(usingRate) {
|
||||
if (usingRate < 60) {
|
||||
return NORMAL_COLOR
|
||||
} else if (usingRate > 60 && usingRate < 80) {
|
||||
return WARNING_COLOR
|
||||
} else if (usingRate > 80) {
|
||||
return DANGER_COLOR
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.el-button--medium {
|
||||
margin: 2px;
|
||||
padding: 10px 10px;
|
||||
}
|
||||
|
||||
.server-monitor-top {
|
||||
padding: 0 10px;
|
||||
text-align: justify-all;
|
||||
}
|
||||
|
||||
.server-monitor-bottom {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.server-information {
|
||||
width: 20%;
|
||||
min-width: 400px;
|
||||
min-height: 300px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.information-instrument-panel {
|
||||
display: inline-block;
|
||||
min-height: 300px;
|
||||
min-width: 400px;
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.server-info-item {
|
||||
display: inline-block;
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.server-info-detail {
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
.server-info-detail-line {
|
||||
margin: 5px;
|
||||
min-height: 20px;
|
||||
}
|
||||
|
||||
.server-info-detail-item {
|
||||
text-align: justify;
|
||||
line-height: 40px;
|
||||
margin: 4px 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.server-monitor-control {
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
line-height: 60px;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.monitor-update-interval {
|
||||
margin: 0 20px;
|
||||
}
|
||||
|
||||
.same-block {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.monitor-update-interval-blank {
|
||||
width: 100px;
|
||||
margin: 0 2px;
|
||||
}
|
||||
|
||||
.monitor-update-interval-unit {
|
||||
width: 80px;
|
||||
margin: 0 2px;
|
||||
}
|
||||
|
||||
.monitor-log-save-time {
|
||||
width: 280px;
|
||||
margin: 0 2px;
|
||||
}
|
||||
|
||||
.clean-monitor-log {
|
||||
}
|
||||
|
||||
|
||||
.server-monitor-line-chart {
|
||||
height: 400px;
|
||||
width: 45%;
|
||||
min-width: 500px;
|
||||
margin: 10px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
Loading…
Reference in New Issue