feat: 运维管理 仪表盘
This commit is contained in:
parent
fd0c3f2eb8
commit
401634be3e
|
@ -0,0 +1,12 @@
|
||||||
|
import server from '@/utils/request';
|
||||||
|
|
||||||
|
export const dashboard = (data: object) =>
|
||||||
|
server.post(`/dashboard/_multi`, data);
|
||||||
|
export const productCount = (data: object) =>
|
||||||
|
server.post(`/device-product/_count`, data);
|
||||||
|
export const getGeo = (data: object) =>
|
||||||
|
server.post(`/geo/object/device/_search/geo.json`, data);
|
||||||
|
export const deviceCount = (data: object) =>
|
||||||
|
server.get(`/device/instance/_count`, data);
|
||||||
|
export const serverNode = (data: object) =>
|
||||||
|
server.get(`/dashboard/cluster/nodes`, data);
|
|
@ -0,0 +1,189 @@
|
||||||
|
<template>
|
||||||
|
<a-spin :spinning="loading">
|
||||||
|
<div class="dash-board">
|
||||||
|
<div class="header">
|
||||||
|
<h3>CPU使用率趋势</h3>
|
||||||
|
<a-range-picker
|
||||||
|
@change="pickerTimeChange"
|
||||||
|
:allowClear="false"
|
||||||
|
:show-time="{ format: 'HH:mm:ss' }"
|
||||||
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
v-model="data.time"
|
||||||
|
>
|
||||||
|
<template #suffixIcon><a-icon type="calendar" /></template>
|
||||||
|
<template #renderExtraFooter>
|
||||||
|
<a-radio-group
|
||||||
|
default-value="a"
|
||||||
|
button-style="solid"
|
||||||
|
style="margin-right: 10px"
|
||||||
|
v-model:value="data.type"
|
||||||
|
>
|
||||||
|
<a-radio-button value="hour">
|
||||||
|
最近1小时
|
||||||
|
</a-radio-button>
|
||||||
|
<a-radio-button value="today">
|
||||||
|
今日
|
||||||
|
</a-radio-button>
|
||||||
|
<a-radio-button value="week">
|
||||||
|
近一周
|
||||||
|
</a-radio-button>
|
||||||
|
</a-radio-group></template
|
||||||
|
>
|
||||||
|
</a-range-picker>
|
||||||
|
</div>
|
||||||
|
<div ref="chartRef" style="width: 100%; height: 300px"></div>
|
||||||
|
</div>
|
||||||
|
</a-spin>
|
||||||
|
</template>
|
||||||
|
m
|
||||||
|
<script lang="ts" setup name="Cpu">
|
||||||
|
import * as echarts from 'echarts';
|
||||||
|
import { dashboard } from '@/api/link/dashboard';
|
||||||
|
import moment from 'moment';
|
||||||
|
import {
|
||||||
|
getTimeFormat,
|
||||||
|
getTimeByType,
|
||||||
|
arrayReverse,
|
||||||
|
defulteParamsData,
|
||||||
|
areaStyleCpu,
|
||||||
|
typeDataLine,
|
||||||
|
} from './tool.ts';
|
||||||
|
|
||||||
|
const chartRef = ref<Record<string, any>>({});
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
const data = ref({
|
||||||
|
type: 'hour',
|
||||||
|
time: [null, null],
|
||||||
|
});
|
||||||
|
|
||||||
|
const pickerTimeChange = () => {
|
||||||
|
data.value.type = undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getCPUEcharts = async (val) => {
|
||||||
|
loading.value = true;
|
||||||
|
const res = await dashboard(defulteParamsData('cpu', val));
|
||||||
|
if (res.success) {
|
||||||
|
const _cpuOptions = {};
|
||||||
|
const _cpuXAxis = new Set();
|
||||||
|
if (res.result?.length) {
|
||||||
|
res.result.forEach((item) => {
|
||||||
|
const value = item.data.value;
|
||||||
|
const nodeID = item.data.clusterNodeId;
|
||||||
|
_cpuXAxis.add(
|
||||||
|
moment(value.timestamp).format(
|
||||||
|
getTimeFormat(data.value.type),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (!_cpuOptions[nodeID]) {
|
||||||
|
_cpuOptions[nodeID] = [];
|
||||||
|
}
|
||||||
|
_cpuOptions[nodeID].push(
|
||||||
|
Number(value.cpuSystemUsage).toFixed(2),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
handleCpuOptions(_cpuOptions, [..._cpuXAxis.keys()]);
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
loading.value = false;
|
||||||
|
}, 300);
|
||||||
|
};
|
||||||
|
|
||||||
|
const setOptions = (optionsData, key) => ({
|
||||||
|
data: arrayReverse(optionsData[key]),
|
||||||
|
name: key,
|
||||||
|
type: 'line',
|
||||||
|
smooth: true,
|
||||||
|
symbol: 'none',
|
||||||
|
areaStyle: areaStyleCpu,
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleCpuOptions = (optionsData, xAxis) => {
|
||||||
|
const chart = chartRef.value;
|
||||||
|
if (chart) {
|
||||||
|
const myChart = echarts.init(chart);
|
||||||
|
const dataKeys = Object.keys(optionsData);
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
boundaryGap: false,
|
||||||
|
data: arrayReverse(xAxis),
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
valueFormatter: (value) => `${value}%`,
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: '50px',
|
||||||
|
right: '50px',
|
||||||
|
},
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: 'inside',
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
color: ['#2CB6E0'],
|
||||||
|
series: dataKeys.length
|
||||||
|
? dataKeys.map((key) => setOptions(optionsData, key))
|
||||||
|
: typeDataLine,
|
||||||
|
};
|
||||||
|
myChart.setOption(options);
|
||||||
|
window.addEventListener('resize', function () {
|
||||||
|
myChart.resize();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => data.value.type,
|
||||||
|
(val) => {
|
||||||
|
const endTime = moment(new Date());
|
||||||
|
const startTime = getTimeByType(val);
|
||||||
|
data.value.time = [startTime, endTime];
|
||||||
|
},
|
||||||
|
{ immediate: true, deep: true },
|
||||||
|
);
|
||||||
|
watch(
|
||||||
|
() => data.value,
|
||||||
|
(val) => {
|
||||||
|
const { time } = val;
|
||||||
|
if (time && Array.isArray(time) && time.length === 2 && time[0]) {
|
||||||
|
getCPUEcharts(val);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true, deep: true },
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.dash-board {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
padding: 24px;
|
||||||
|
background-color: #fff;
|
||||||
|
box-shadow: 0px 2.73036px 5.46071px rgba(31, 89, 245, 0.2);
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
h3 {
|
||||||
|
width: 200px;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,196 @@
|
||||||
|
<template>
|
||||||
|
<a-spin :spinning="loading">
|
||||||
|
<div class="dash-board">
|
||||||
|
<div class="header">
|
||||||
|
<h3>JVM内存使用率趋势</h3>
|
||||||
|
<a-range-picker
|
||||||
|
@change="pickerTimeChange"
|
||||||
|
:allowClear="false"
|
||||||
|
:show-time="{ format: 'HH:mm:ss' }"
|
||||||
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
v-model="data.time"
|
||||||
|
>
|
||||||
|
<template #suffixIcon><a-icon type="calendar" /></template>
|
||||||
|
<template #renderExtraFooter>
|
||||||
|
<a-radio-group
|
||||||
|
default-value="a"
|
||||||
|
button-style="solid"
|
||||||
|
style="margin-right: 10px"
|
||||||
|
v-model:value="data.type"
|
||||||
|
>
|
||||||
|
<a-radio-button value="hour">
|
||||||
|
最近1小时
|
||||||
|
</a-radio-button>
|
||||||
|
<a-radio-button value="today">
|
||||||
|
今日
|
||||||
|
</a-radio-button>
|
||||||
|
<a-radio-button value="week">
|
||||||
|
近一周
|
||||||
|
</a-radio-button>
|
||||||
|
</a-radio-group></template
|
||||||
|
>
|
||||||
|
</a-range-picker>
|
||||||
|
</div>
|
||||||
|
<div ref="chartRef" style="width: 100%; height: 300px"></div>
|
||||||
|
</div>
|
||||||
|
</a-spin>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup name="Jvm">
|
||||||
|
import * as echarts from 'echarts';
|
||||||
|
import { dashboard } from '@/api/link/dashboard';
|
||||||
|
import moment from 'moment';
|
||||||
|
import {
|
||||||
|
getTimeFormat,
|
||||||
|
getTimeByType,
|
||||||
|
arrayReverse,
|
||||||
|
typeDataLine,
|
||||||
|
areaStyleJvm,
|
||||||
|
defulteParamsData,
|
||||||
|
} from './tool.ts';
|
||||||
|
|
||||||
|
const chartRef = ref<Record<string, any>>({});
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
const data = ref({
|
||||||
|
type: 'hour',
|
||||||
|
time: [null, null],
|
||||||
|
});
|
||||||
|
|
||||||
|
const pickerTimeChange = () => {
|
||||||
|
data.value.type = undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getJVMEcharts = async (val) => {
|
||||||
|
loading.value = true;
|
||||||
|
const res = await dashboard(defulteParamsData('jvm', val));
|
||||||
|
if (res.success) {
|
||||||
|
const _jvmOptions = {};
|
||||||
|
const _jvmXAxis = new Set();
|
||||||
|
if (res.result?.length) {
|
||||||
|
res.result.forEach((item) => {
|
||||||
|
const value = item.data.value;
|
||||||
|
const memoryJvmHeapFree = value.memoryJvmHeapFree;
|
||||||
|
const memoryJvmHeapTotal = value.memoryJvmHeapTotal;
|
||||||
|
const nodeID = item.data.clusterNodeId;
|
||||||
|
|
||||||
|
const _value = (
|
||||||
|
((memoryJvmHeapTotal - memoryJvmHeapFree) /
|
||||||
|
memoryJvmHeapTotal) *
|
||||||
|
100
|
||||||
|
).toFixed(2);
|
||||||
|
if (!_jvmOptions[nodeID]) {
|
||||||
|
_jvmOptions[nodeID] = [];
|
||||||
|
}
|
||||||
|
_jvmXAxis.add(
|
||||||
|
moment(value.timestamp).format(
|
||||||
|
getTimeFormat(data.value.type),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
_jvmOptions[nodeID].push(_value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
handleJVMOptions(_jvmOptions, [..._jvmXAxis.keys()]);
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
loading.value = false;
|
||||||
|
}, 300);
|
||||||
|
};
|
||||||
|
|
||||||
|
const setOptions = (optionsData, key) => ({
|
||||||
|
data: arrayReverse(optionsData[key]),
|
||||||
|
name: key,
|
||||||
|
type: 'line',
|
||||||
|
smooth: true,
|
||||||
|
symbol: 'none',
|
||||||
|
areaStyle: areaStyleJvm,
|
||||||
|
});
|
||||||
|
const handleJVMOptions = (optionsData, xAxis) => {
|
||||||
|
const chart = chartRef.value;
|
||||||
|
if (chart) {
|
||||||
|
const myChart = echarts.init(chart);
|
||||||
|
const dataKeys = Object.keys(optionsData);
|
||||||
|
|
||||||
|
console.log(21, arrayReverse(xAxis), xAxis);
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
boundaryGap: false,
|
||||||
|
data: arrayReverse(xAxis),
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
valueFormatter: (value: any) => `${value}%`,
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: '50px',
|
||||||
|
right: '50px',
|
||||||
|
},
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: 'inside',
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
color: ['#60DFC7'],
|
||||||
|
series: dataKeys.length
|
||||||
|
? dataKeys.map((key) => setOptions(optionsData, key))
|
||||||
|
: typeDataLine,
|
||||||
|
};
|
||||||
|
myChart.setOption(options);
|
||||||
|
window.addEventListener('resize', function () {
|
||||||
|
myChart.resize();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => data.value.type,
|
||||||
|
(val) => {
|
||||||
|
const endTime = moment(new Date());
|
||||||
|
const startTime = getTimeByType(val);
|
||||||
|
data.value.time = [startTime, endTime];
|
||||||
|
},
|
||||||
|
{ immediate: true, deep: true },
|
||||||
|
);
|
||||||
|
watch(
|
||||||
|
() => data.value,
|
||||||
|
(val) => {
|
||||||
|
const { time } = val;
|
||||||
|
if (time && Array.isArray(time) && time.length === 2 && time[0]) {
|
||||||
|
getJVMEcharts(val);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true, deep: true },
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.dash-board {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
padding: 24px;
|
||||||
|
background-color: #fff;
|
||||||
|
box-shadow: 0px 2.73036px 5.46071px rgba(31, 89, 245, 0.2);
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
h3 {
|
||||||
|
width: 200px;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,222 @@
|
||||||
|
<template>
|
||||||
|
<a-spin :spinning="loading">
|
||||||
|
<div class="dash-board">
|
||||||
|
<div class="header">
|
||||||
|
<div class="left">
|
||||||
|
<h3 style="width: 80px">网络流量</h3>
|
||||||
|
<a-radio-group
|
||||||
|
button-style="solid"
|
||||||
|
v-model:value="data.type"
|
||||||
|
>
|
||||||
|
<a-radio-button value="bytesRead">
|
||||||
|
上行
|
||||||
|
</a-radio-button>
|
||||||
|
<a-radio-button value="bytesSent">
|
||||||
|
下行
|
||||||
|
</a-radio-button>
|
||||||
|
</a-radio-group>
|
||||||
|
</div>
|
||||||
|
<div class="right">
|
||||||
|
<a-radio-group
|
||||||
|
default-value="a"
|
||||||
|
button-style="solid"
|
||||||
|
style="margin-right: 10px"
|
||||||
|
v-model:value="data.time.type"
|
||||||
|
>
|
||||||
|
<a-radio-button value="hour">
|
||||||
|
最近1小时
|
||||||
|
</a-radio-button>
|
||||||
|
<a-radio-button value="today"> 今日 </a-radio-button>
|
||||||
|
<a-radio-button value="week"> 近一周 </a-radio-button>
|
||||||
|
</a-radio-group>
|
||||||
|
<a-range-picker
|
||||||
|
:allowClear="false"
|
||||||
|
:show-time="{ format: 'HH:mm:ss' }"
|
||||||
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
v-model="data.time.time"
|
||||||
|
@change="pickerTimeChange"
|
||||||
|
>
|
||||||
|
<template #suffixIcon
|
||||||
|
><a-icon type="calendar"
|
||||||
|
/></template>
|
||||||
|
</a-range-picker>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
ref="chartRef"
|
||||||
|
v-if="flag"
|
||||||
|
style="width: 100%; height: 350px"
|
||||||
|
></div>
|
||||||
|
<a-empty v-else style="height: 300px; margin-top: 120px" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-spin>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup name="Network">
|
||||||
|
import { dashboard } from '@/api/link/dashboard';
|
||||||
|
import {
|
||||||
|
getTimeByType,
|
||||||
|
typeDataLine,
|
||||||
|
areaStyle,
|
||||||
|
networkParams,
|
||||||
|
arrayReverse,
|
||||||
|
} from './tool.ts';
|
||||||
|
import moment from 'moment';
|
||||||
|
import * as echarts from 'echarts';
|
||||||
|
|
||||||
|
const chartRef = ref<Record<string, any>>({});
|
||||||
|
|
||||||
|
const flag = ref(true);
|
||||||
|
const loading = ref(false);
|
||||||
|
const myChart = ref(null);
|
||||||
|
const data = ref({
|
||||||
|
type: 'bytesRead',
|
||||||
|
time: {
|
||||||
|
type: 'today',
|
||||||
|
time: [null, null],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const pickerTimeChange = () => {
|
||||||
|
data.value.time.type = undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getNetworkEcharts = async (val) => {
|
||||||
|
loading.value = true;
|
||||||
|
const resp = await dashboard(networkParams(val));
|
||||||
|
if (resp.success) {
|
||||||
|
const _networkOptions = {};
|
||||||
|
const _networkXAxis = new Set();
|
||||||
|
if (resp.result.length) {
|
||||||
|
resp.result.forEach((item) => {
|
||||||
|
const value = item.data.value;
|
||||||
|
const _data = [];
|
||||||
|
const nodeID = item.data.clusterNodeId;
|
||||||
|
value.forEach((item) => {
|
||||||
|
_data.push(item.value);
|
||||||
|
_networkXAxis.add(item.timeString);
|
||||||
|
});
|
||||||
|
_networkOptions[nodeID] = {
|
||||||
|
_data: _networkOptions[nodeID]
|
||||||
|
? _networkOptions[nodeID]._data.concat(_data)
|
||||||
|
: _data,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
handleNetworkOptions(_networkOptions, [..._networkXAxis.keys()]);
|
||||||
|
} else {
|
||||||
|
handleNetworkOptions([], []);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
loading.value = false;
|
||||||
|
}, 300);
|
||||||
|
};
|
||||||
|
const networkValueRender = (obj) => {
|
||||||
|
const { value } = obj;
|
||||||
|
let _data = '';
|
||||||
|
if (value >= 1024 && value < 1024 * 1024) {
|
||||||
|
_data = `${Number((value / 1024).toFixed(2))}KB`;
|
||||||
|
} else if (value >= 1024 * 1024) {
|
||||||
|
_data = `${Number((value / 1024 / 1024).toFixed(2))}M`;
|
||||||
|
} else {
|
||||||
|
_data = `${value}B`;
|
||||||
|
}
|
||||||
|
return `${obj?.axisValueLabel}<br />${obj?.marker}${obj?.seriesName}: ${_data}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const setOptions = (data, key) => ({
|
||||||
|
data: data[key]._data, // .map((item) => Number((item / 1024 / 1024).toFixed(2))),
|
||||||
|
name: key,
|
||||||
|
type: 'line',
|
||||||
|
smooth: true,
|
||||||
|
areaStyle,
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleNetworkOptions = (optionsData, xAxis) => {
|
||||||
|
const chart = chartRef.value;
|
||||||
|
|
||||||
|
if (chart) {
|
||||||
|
const myChart = echarts.init(chart);
|
||||||
|
const dataKeys = Object.keys(optionsData);
|
||||||
|
const options = {
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
boundaryGap: false,
|
||||||
|
data: xAxis,
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: '80px',
|
||||||
|
right: '50px',
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
formatter: (_value) => networkValueRender(_value[0]),
|
||||||
|
},
|
||||||
|
color: ['#979AFF'],
|
||||||
|
series: dataKeys.length
|
||||||
|
? dataKeys.map((key) => setOptions(optionsData, key))
|
||||||
|
: typeDataLine,
|
||||||
|
};
|
||||||
|
myChart.setOption(options);
|
||||||
|
window.addEventListener('resize', function () {
|
||||||
|
myChart.resize();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => data.value.time.type,
|
||||||
|
(value) => {
|
||||||
|
const endTime = moment(new Date());
|
||||||
|
const startTime = getTimeByType(value);
|
||||||
|
data.value.time.time = [startTime, endTime];
|
||||||
|
},
|
||||||
|
{ immediate: true, deep: true },
|
||||||
|
);
|
||||||
|
watch(
|
||||||
|
() => data.value,
|
||||||
|
(value) => {
|
||||||
|
const {
|
||||||
|
time: { time },
|
||||||
|
} = value;
|
||||||
|
if (time && Array.isArray(time) && time.length === 2 && time[0]) {
|
||||||
|
getNetworkEcharts(value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true, deep: true },
|
||||||
|
);
|
||||||
|
// onMounted(() => {
|
||||||
|
// createEcharts();
|
||||||
|
// });
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.dash-board {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
padding: 24px;
|
||||||
|
background-color: #fff;
|
||||||
|
box-shadow: 0px 2.73036px 5.46071px rgba(31, 89, 245, 0.2);
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
.left h3 {
|
||||||
|
width: 200px;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.left,
|
||||||
|
.right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,155 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<a-select
|
||||||
|
style="width: 300px; margin-bottom: 20px"
|
||||||
|
@change="serverIdChange"
|
||||||
|
:value="serverId"
|
||||||
|
:options="serverNode"
|
||||||
|
v-if="serverNode.length > 1"
|
||||||
|
></a-select>
|
||||||
|
<div class="dash-board">
|
||||||
|
<div class="dash-board-item">
|
||||||
|
<TopEchartsItemNode title="CPU使用率" :value="topValues.cpu" />
|
||||||
|
</div>
|
||||||
|
<div class="dash-board-item">
|
||||||
|
<TopEchartsItemNode
|
||||||
|
title="JVM内存"
|
||||||
|
:max="topValues.jvmTotal"
|
||||||
|
:bottom="`总JVM内存 ${topValues.jvmTotal}G`"
|
||||||
|
formatter="G"
|
||||||
|
:value="topValues.jvm"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="dash-board-item">
|
||||||
|
<TopEchartsItemNode
|
||||||
|
title="磁盘占用"
|
||||||
|
:max="topValues.usageTotal"
|
||||||
|
:bottom="`总磁盘大小 ${topValues.usageTotal}G`"
|
||||||
|
formatter="G"
|
||||||
|
:value="topValues.usage"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="dash-board-item">
|
||||||
|
<TopEchartsItemNode
|
||||||
|
title="系统内存"
|
||||||
|
:max="topValues.systemUsageTotal"
|
||||||
|
:bottom="`系统内存 ${topValues.systemUsageTotal}G`"
|
||||||
|
formatter="G"
|
||||||
|
:value="topValues.systemUsage"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { serverNode } from '@/api/link/dashboard';
|
||||||
|
import TopEchartsItemNode from './TopEchartsItemNode.vue';
|
||||||
|
import { getWebSocket } from '@/utils/websocket';
|
||||||
|
import { map } from 'rxjs/operators';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'TopCard',
|
||||||
|
components: { TopEchartsItemNode },
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
serverId: '',
|
||||||
|
serverNode: [],
|
||||||
|
topValues: {
|
||||||
|
cpu: 0,
|
||||||
|
jvm: 0,
|
||||||
|
jvmTotal: 0,
|
||||||
|
usage: 0,
|
||||||
|
usageTotal: 0,
|
||||||
|
systemUsage: 0,
|
||||||
|
systemUsageTotal: 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
serverNode().then((resp) => {
|
||||||
|
if (resp.success) {
|
||||||
|
this.serverNode = resp.result.map((item) => ({
|
||||||
|
label: item.name,
|
||||||
|
value: item.id,
|
||||||
|
}));
|
||||||
|
if (this.serverNode && this.serverNode.length) {
|
||||||
|
this.serverId = this.serverNode[0].value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
serverId: {
|
||||||
|
handler(val) {
|
||||||
|
if (val) {
|
||||||
|
this.getData(val);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
serverIdChange(val) {
|
||||||
|
this.serverId = val;
|
||||||
|
},
|
||||||
|
getData() {
|
||||||
|
const id = 'operations-statistics-system-info-realTime';
|
||||||
|
const topic = '/dashboard/systemMonitor/stats/info/realTime';
|
||||||
|
getWebSocket(id, topic, {
|
||||||
|
type: 'all',
|
||||||
|
serverNodeId: this.serverId,
|
||||||
|
interval: '1s',
|
||||||
|
agg: 'avg',
|
||||||
|
})
|
||||||
|
.pipe(map((res) => res.payload))
|
||||||
|
.subscribe((payload) => {
|
||||||
|
const value = payload.value;
|
||||||
|
const cpu = value.cpu;
|
||||||
|
const memory = value.memory;
|
||||||
|
const disk = value.disk;
|
||||||
|
this.topValues = {
|
||||||
|
cpu: cpu.systemUsage,
|
||||||
|
jvm: Number(
|
||||||
|
(
|
||||||
|
(memory.jvmHeapUsage / 100) *
|
||||||
|
(memory.jvmHeapTotal / 1024)
|
||||||
|
).toFixed(1),
|
||||||
|
),
|
||||||
|
jvmTotal: Math.ceil(memory.jvmHeapTotal / 1024),
|
||||||
|
usage: Number(
|
||||||
|
((disk.total / 1024) * (disk.usage / 100)).toFixed(
|
||||||
|
1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
usageTotal: Math.ceil(disk.total / 1024),
|
||||||
|
systemUsage: Number(
|
||||||
|
(
|
||||||
|
(memory.systemTotal / 1024) *
|
||||||
|
(memory.systemUsage / 100)
|
||||||
|
).toFixed(1),
|
||||||
|
),
|
||||||
|
systemUsageTotal: Math.ceil(memory.systemTotal / 1024),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.dash-board {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
height: 100%;
|
||||||
|
background-color: #fff;
|
||||||
|
box-shadow: 0px 2.73036px 5.46071px rgba(31, 89, 245, 0.2);
|
||||||
|
border-radius: 2px;
|
||||||
|
justify-content: space-between;
|
||||||
|
.dash-board-item {
|
||||||
|
flex: 1;
|
||||||
|
margin: 24px 12px;
|
||||||
|
min-width: 250px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,282 @@
|
||||||
|
<template>
|
||||||
|
<div class="echarts-item">
|
||||||
|
<div class="echarts-item-left">
|
||||||
|
<div class="echarts-item-title">{{ title }}</div>
|
||||||
|
<div class="echarts-item-value">
|
||||||
|
{{ value || 0 }} {{ formatter || '%' }}
|
||||||
|
</div>
|
||||||
|
<div v-if="!!bottom" class="echarts-item-bottom">{{ bottom }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="echarts-item-right">
|
||||||
|
<div ref="chartRef" style="width: 100%; height: 100px"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import * as echarts from 'echarts';
|
||||||
|
export default {
|
||||||
|
name: 'TopEchartsItemNode',
|
||||||
|
props: {
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
max: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
bottom: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
formatter: {
|
||||||
|
type: String,
|
||||||
|
default: '%',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
options: {},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
createChart(val) {
|
||||||
|
const chart = this.$refs.chartRef;
|
||||||
|
if (chart && Object.keys(val).length > 0) {
|
||||||
|
const myChart = echarts.init(chart);
|
||||||
|
myChart.setOption(val);
|
||||||
|
window.addEventListener('resize', function () {
|
||||||
|
myChart.resize();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getOptions(max, formatter, val) {
|
||||||
|
let formatterCount = 0;
|
||||||
|
this.options = {
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
type: 'gauge',
|
||||||
|
min: 0,
|
||||||
|
max: max || 100,
|
||||||
|
startAngle: 200,
|
||||||
|
endAngle: -20,
|
||||||
|
center: ['50%', '67%'],
|
||||||
|
title: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
axisTick: {
|
||||||
|
distance: -20,
|
||||||
|
lineStyle: {
|
||||||
|
width: 1,
|
||||||
|
color: 'rgba(0,0,0,0.15)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
distance: -22,
|
||||||
|
length: 9,
|
||||||
|
lineStyle: {
|
||||||
|
width: 1,
|
||||||
|
color: '#000',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
distance: -22,
|
||||||
|
color: 'auto',
|
||||||
|
fontSize: 12,
|
||||||
|
width: 30,
|
||||||
|
padding: [6, 10, 0, 10],
|
||||||
|
formatter: (value) => {
|
||||||
|
formatterCount += 1;
|
||||||
|
if ([1, 3, 6, 9, 11].includes(formatterCount)) {
|
||||||
|
return value + (formatter || '%');
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pointer: {
|
||||||
|
length: '80%',
|
||||||
|
width: 4,
|
||||||
|
itemStyle: {
|
||||||
|
color: 'auto',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
anchor: {
|
||||||
|
show: true,
|
||||||
|
showAbove: true,
|
||||||
|
size: 20,
|
||||||
|
itemStyle: {
|
||||||
|
borderWidth: 3,
|
||||||
|
borderColor: '#fff',
|
||||||
|
shadowBlur: 20,
|
||||||
|
shadowColor: 'rgba(0, 0, 0, .25)',
|
||||||
|
color: 'auto',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
axisLine: {
|
||||||
|
lineStyle: {
|
||||||
|
width: 10,
|
||||||
|
color: [
|
||||||
|
[0.25, 'rgba(36, 178, 118, 1)'],
|
||||||
|
[
|
||||||
|
0.4,
|
||||||
|
new echarts.graphic.LinearGradient(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
offset: 0,
|
||||||
|
color: 'rgba(66, 147, 255, 1)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: 'rgba(36, 178, 118, 1)',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.5,
|
||||||
|
new echarts.graphic.LinearGradient(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
offset: 0,
|
||||||
|
color: 'rgba(250, 178, 71, 1)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: 'rgba(66, 147, 255, 1)',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
new echarts.graphic.LinearGradient(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
[
|
||||||
|
{
|
||||||
|
offset: 0,
|
||||||
|
color: 'rgba(250, 178, 71, 1)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: 'rgba(247, 111, 93, 1)',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
detail: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
data: [{ value: val || 0 }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
options: {
|
||||||
|
handler(val) {
|
||||||
|
this.createChart(val);
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
deep: true,
|
||||||
|
},
|
||||||
|
max: {
|
||||||
|
handler(val) {
|
||||||
|
this.getOptions(val, this.formatter, this.value);
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
deep: true,
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
handler(val) {
|
||||||
|
this.getOptions(this.max, this.formatter, val);
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
deep: true,
|
||||||
|
},
|
||||||
|
formatter: {
|
||||||
|
handler(val) {
|
||||||
|
this.getOptions(this.max, val, this.value);
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
deep: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.echarts-item {
|
||||||
|
display: flex;
|
||||||
|
height: 150px;
|
||||||
|
padding: 16px;
|
||||||
|
background-color: #fff;
|
||||||
|
box-shadow: 0px 2.73036px 5.46071px rgba(31, 89, 245, 0.2);
|
||||||
|
|
||||||
|
.echarts-item-left {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 45%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.echarts-item-right {
|
||||||
|
width: 55%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.echarts-item-title {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
color: rgba(#000, 0.6);
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.echarts-item-value {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 36px;
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-align: left;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.echarts-item-bottom {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-grow: 1;
|
||||||
|
justify-content: center;
|
||||||
|
height: 0;
|
||||||
|
padding-left: 12px;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 0;
|
||||||
|
width: 4px;
|
||||||
|
height: 12px;
|
||||||
|
background-color: #ff595e;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
content: ' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,125 @@
|
||||||
|
import moment from 'moment';
|
||||||
|
import * as echarts from 'echarts';
|
||||||
|
|
||||||
|
export const getInterval = (type) => {
|
||||||
|
switch (type) {
|
||||||
|
case 'year':
|
||||||
|
return '30d';
|
||||||
|
case 'month':
|
||||||
|
case 'week':
|
||||||
|
return '1d';
|
||||||
|
case 'hour':
|
||||||
|
return '1m';
|
||||||
|
default:
|
||||||
|
return '1h';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export const getTimeFormat = (type) => {
|
||||||
|
switch (type) {
|
||||||
|
case 'year':
|
||||||
|
return 'YYYY-MM-DD';
|
||||||
|
case 'month':
|
||||||
|
case 'week':
|
||||||
|
return 'MM-DD';
|
||||||
|
case 'hour':
|
||||||
|
return 'HH:mm';
|
||||||
|
default:
|
||||||
|
return 'HH';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getTimeByType = (type) => {
|
||||||
|
switch (type) {
|
||||||
|
case 'hour':
|
||||||
|
return moment().subtract(1, 'hours');
|
||||||
|
case 'week':
|
||||||
|
return moment().subtract(6, 'days');
|
||||||
|
case 'month':
|
||||||
|
return moment().subtract(29, 'days');
|
||||||
|
case 'year':
|
||||||
|
return moment().subtract(365, 'days');
|
||||||
|
default:
|
||||||
|
return moment().startOf('day');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const arrayReverse = (data) => {
|
||||||
|
const newArray = [];
|
||||||
|
for (let i = data.length - 1; i >= 0; i--) {
|
||||||
|
newArray.push(data[i]);
|
||||||
|
}
|
||||||
|
return newArray;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const networkParams = (val) => [
|
||||||
|
{
|
||||||
|
dashboard: 'systemMonitor',
|
||||||
|
object: 'network',
|
||||||
|
measurement: 'traffic',
|
||||||
|
dimension: 'agg',
|
||||||
|
group: 'network',
|
||||||
|
params: {
|
||||||
|
type: val.type,
|
||||||
|
interval: getInterval(val.time.type),
|
||||||
|
from: moment(val.time.time[0]).valueOf(),
|
||||||
|
to: moment(val.time.time[1]).valueOf(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
export const defulteParamsData = (group, val) => [
|
||||||
|
{
|
||||||
|
dashboard: 'systemMonitor',
|
||||||
|
object: 'stats',
|
||||||
|
measurement: 'info',
|
||||||
|
dimension: 'history',
|
||||||
|
group,
|
||||||
|
params: {
|
||||||
|
from: moment(val.time[0]).valueOf(),
|
||||||
|
to: moment(val.time[1]).valueOf(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const areaStyle = {
|
||||||
|
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: 'rgba(151, 154, 255, 0)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 0,
|
||||||
|
color: 'rgba(151, 154, 255, .24)',
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
};
|
||||||
|
export const areaStyleCpu = {
|
||||||
|
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: 'rgba(44, 182, 224, 0)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 0,
|
||||||
|
color: 'rgba(44, 182, 224, .24)',
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
};
|
||||||
|
export const areaStyleJvm = {
|
||||||
|
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: 'rgba(96, 223, 199, 0)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 0,
|
||||||
|
color: 'rgba(96, 223, 199, .24)',
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const typeDataLine = [
|
||||||
|
{
|
||||||
|
data: [],
|
||||||
|
type: 'line',
|
||||||
|
},
|
||||||
|
];
|
|
@ -0,0 +1,25 @@
|
||||||
|
<template>
|
||||||
|
<page-container>
|
||||||
|
<div>
|
||||||
|
<a-row :gutter="[24, 24]">
|
||||||
|
<a-col :span="24"><TopCard /> </a-col>
|
||||||
|
<a-col :span="24"><Network /></a-col>
|
||||||
|
<a-col :span="12"><Cpu /></a-col>
|
||||||
|
<a-col :span="12"><Jvm /></a-col>
|
||||||
|
</a-row>
|
||||||
|
</div>
|
||||||
|
</page-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import TopCard from './components/TopCard.vue';
|
||||||
|
import Network from './components/Network.vue';
|
||||||
|
import Cpu from './components/Cpu.vue';
|
||||||
|
import Jvm from './components/Jvm.vue';
|
||||||
|
export default {
|
||||||
|
name: 'LinkDashboard',
|
||||||
|
components: { Cpu, Jvm, Network, TopCard },
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped></style>
|
Loading…
Reference in New Issue