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