feat: 资源监控信息

This commit is contained in:
TsMask
2023-11-01 17:45:21 +08:00
parent a824e64063
commit dcbd75b8c9
4 changed files with 644 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
import { request } from '@/plugins/http-fetch';
/**资源监控信息加载 */
export function getLoad(query: Record<string, any>) {
return request({
url: '/monitor/load',
method: 'get',
params: query,
});
}

View File

@@ -72,3 +72,18 @@ export function diffValue(
if (Number.isNaN(value)) return 0;
return value;
}
/**
* 格式时间不带年份
*
* 年-月\n时:分 列如10-13 \n 15:13
* @returns MM-DD\nHH:mm
*/
export function parseDateWithoutYear(date: string | number | Date) {
const day = dayjs(date);
const M: string = `${day.month() + 1}`.padStart(2, '0');
const D: string = `${day.date()}`.padStart(2, '0');
const H: string = `${day.hour()}`.padStart(2, '0');
const m: string = `${day.minute()}`.padStart(2, '0');
return `${M}-${D}\n${H}:${m}`;
}

View File

@@ -91,3 +91,30 @@ export function parseObjLineToHump(obj: any): any {
}
return obj;
}
/**
* 转换磁盘容量
* @param size 数值大小
* @returns
*/
export function parseSizeFromMB(size: number): string {
const num = 1024.0;
if (size < num) return size + ' MB';
if (size < Math.pow(num, 2)) return (size / num).toFixed(2) + ' GB';
return (size / Math.pow(num, 3)).toFixed(2) + ' TB';
}
/**
* 转换网络速率
* @param size 数值大小
* @returns
*/
export function parseSizeFromKBs(size: number): string {
const num = 1024.0;
if (size < num) return size + ' KB/s';
if (size < Math.pow(num, 2)) return (size / num).toFixed(2) + ' MB/s';
if (size < Math.pow(num, 3)) {
return (size / Math.pow(num, 2)).toFixed(2) + ' GB/s';
}
return (size / Math.pow(num, 3)).toFixed(2) + ' TB/s';
}

View File

@@ -0,0 +1,592 @@
<script setup lang="ts">
import { reactive, onMounted } from 'vue';
import { PageContainer } from '@ant-design-vue/pro-layout';
import ChartLine from '@/components/ChartLine/index.vue';
import { getLoad } from '@/api/monitor/monitor';
import { RESULT_CODE_SUCCESS } from '@/constants/result-constants';
import useI18n from '@/hooks/useI18n';
import {
YYYY_MM_DD_HH_MM_SS,
parseDateToStr,
parseDateWithoutYear,
parseStrToDate,
} from '@/utils/date-utils';
import { parseSizeFromKBs } from '@/utils/parse-utils';
const { t } = useI18n();
/**开始结束时间类型 */
type RangePickerType = {
/**全局时间 */
all: [string, string];
/**平均负载 */
load: [string, string];
/**CPU */
cpu: [string, string];
/**内存 */
memory: [string, string];
/**磁盘 */
io: [string, string];
/**网络 */
network: [string, string];
};
/**开始结束时间 */
let rangePicker = reactive<RangePickerType>({
all: ['', ''],
load: ['', ''],
cpu: ['', ''],
memory: ['', ''],
io: ['', ''],
network: ['', ''],
});
/**查询全部资源数据列表 */
function fnGetList() {
let startTime = null;
let endTime = null;
const dateString = rangePicker.all;
if (dateString[0]) {
startTime = parseStrToDate(dateString[0], YYYY_MM_DD_HH_MM_SS);
endTime = parseStrToDate(dateString[1], YYYY_MM_DD_HH_MM_SS);
} else {
const now = new Date();
now.setHours(0, 0, 0, 0);
startTime = now;
endTime = new Date();
}
getLoad({
type: 'all',
startTime: startTime.getTime(),
endTime: endTime.getTime(),
}).then(res => {
if (res.code === RESULT_CODE_SUCCESS && res.data) {
fnLoadChart(res.data.base);
fnCPUChart(res.data.base);
fnMemoryChart(res.data.base);
fnIOChart(res.data.io);
fnNetworkChart(res.data.network);
}
});
// 设置初始时间段
const initRangePicker: [string, string] = [
parseDateToStr(startTime, YYYY_MM_DD_HH_MM_SS),
parseDateToStr(endTime, YYYY_MM_DD_HH_MM_SS),
];
rangePicker.all = initRangePicker;
rangePicker.load = initRangePicker;
rangePicker.cpu = initRangePicker;
rangePicker.memory = initRangePicker;
rangePicker.io = initRangePicker;
rangePicker.network = initRangePicker;
}
/**图表显示数据 */
const chartsOption = reactive({
/**平均负载 */
loadChart: {},
/**CPU */
CPUChart: {},
/**内存 */
memoryChart: {},
/**IO */
ioChart: {},
/**网络 */
networkChart: {},
});
/**初始图表数据-平均负载 */
function fnLoadChart(data: Record<string, any>[]) {
let loadDate: string[] = [];
let load1Data: string[] = [];
let load5Data: string[] = [];
let load15Data: string[] = [];
let loadUsage: string[] = [];
for (const item of data) {
loadDate.push(parseDateWithoutYear(item.createTime));
load1Data.push(Number(item.cpuLoad1).toFixed(2));
load5Data.push(Number(item.cpuLoad5).toFixed(2));
load15Data.push(Number(item.cpuLoad15).toFixed(2));
loadUsage.push(Number(item.loadUsage).toFixed(2));
}
// 图标参数
const option = {
xDatas: loadDate,
yDatas: [
{
name: `1${t('common.units.minute')}`,
data: load1Data,
},
{
name: `5${t('common.units.minute')}`,
data: load5Data,
},
{
name: `15${t('common.units.minute')}`,
data: load15Data,
},
{
name: t('views.monitor.monitor.resourceUsage'),
data: loadUsage,
yAxisIndex: 1,
},
],
yAxis: [
{ type: 'value', name: `${t('views.monitor.monitor.loadDetail')} ( % )` },
{
type: 'value',
name: `${t('views.monitor.monitor.resourceUsage')} ( % )`,
position: 'right',
alignTicks: true,
},
],
grid: { left: '5%', right: '5%', bottom: '20%' },
formatStr: '%',
};
chartsOption.loadChart = option;
}
/**初始图表数据-CPU */
function fnCPUChart(data: Record<string, any>[]) {
let cpuDate: string[] = [];
let cpuData: string[] = [];
for (const item of data) {
cpuDate.push(parseDateWithoutYear(item.createTime));
cpuData.push(Number(item.cpu).toFixed(2));
}
// 图标参数
const option = {
xDatas: cpuDate,
yDatas: [
{
name: 'CPU',
data: cpuData,
},
],
grid: {
left: '5%',
right: '5%',
bottom: '20%',
},
formatStr: '%',
};
chartsOption.CPUChart = option;
}
/**初始图表数据-内存 */
function fnMemoryChart(data: Record<string, any>[]) {
let memoryDate: string[] = [];
let memoryData: number[] = [];
for (const item of data) {
memoryDate.push(parseDateWithoutYear(item.createTime));
memoryData.push(Number(item.memory));
}
// 图标参数
const option = {
xDatas: memoryDate,
yDatas: [
{
name: t('views.monitor.monitor.memory'),
data: memoryData,
},
],
grid: {
left: '5%',
right: '5%',
bottom: '20%',
},
formatStr: '%',
};
chartsOption.memoryChart = option;
}
/**初始图表数据-IO */
function fnIOChart(data: Record<string, any>[]) {
let ioDate: string[] = [];
let ioRead: string[] = [];
let ioWrite: string[] = [];
let ioCount: number[] = [];
let ioTime: number[] = [];
for (const item of data) {
ioDate.push(parseDateWithoutYear(item.createTime));
ioRead.push(Number(item.read / 1024).toFixed(2));
ioWrite.push(Number(item.write / 1024).toFixed(2));
ioCount.push(Number(item.count));
ioTime.push(Number(item.time));
}
// 图标参数
const option = {
xDatas: ioDate,
yDatas: [
{
name: t('views.monitor.monitor.read'),
data: ioRead,
},
{
name: t('views.monitor.monitor.write'),
data: ioWrite,
},
{
name: t('views.monitor.monitor.readWriteCount'),
data: ioCount,
yAxisIndex: 1,
},
{
name: t('views.monitor.monitor.readWriteTime'),
data: ioTime,
yAxisIndex: 1,
},
],
tooltip: {
trigger: 'axis',
formatter: function (datas: any) {
let res = datas[0].name + '<br/>';
for (const item of datas) {
if (
item.seriesName === t('views.monitor.monitor.read') ||
item.seriesName === t('views.monitor.monitor.write')
) {
res += `${item.marker} ${item.seriesName}${parseSizeFromKBs(
item.data
)}<br/>`;
}
if (item.seriesName === t('views.monitor.monitor.readWriteCount')) {
res += `${item.marker} ${item.seriesName}${item.data} ${t(
'views.monitor.monitor.count'
)}/s<br/>`;
}
if (item.seriesName === t('views.monitor.monitor.readWriteTime')) {
res += `${item.marker} ${item.seriesName}${item.data} ms<br/>`;
}
}
return res;
},
},
grid: {
left: '5%',
right: '5%',
bottom: '20%',
},
yAxis: [
{ type: 'value', name: '( KB/s )', axisLabel: { fontSize: 10 } },
{
type: 'value',
position: 'right',
alignTicks: true,
axisLabel: {
fontSize: 10,
},
},
],
};
chartsOption.ioChart = option;
}
/**初始图表数据-Network */
function fnNetworkChart(data: Record<string, any>[]) {
let networkDate: string[] = [];
let networkUp: string[] = [];
let networkDown: string[] = [];
for (const item of data) {
networkDate.push(parseDateWithoutYear(item.createTime));
networkUp.push(Number(item.up).toFixed(2));
networkDown.push(Number(item.down).toFixed(2));
}
// 图标参数
const option = {
xDatas: networkDate,
yDatas: [
{
name: t('views.monitor.monitor.up'),
data: networkUp,
},
{
name: t('views.monitor.monitor.down'),
data: networkDown,
},
],
grid: {
left: '5%',
right: '5%',
bottom: '20%',
},
formatStr: 'KB/s',
};
chartsOption.networkChart = option;
}
/**查询数据-平均负载/CPU/内存 */
function fnGetQuery(
type: 'load' | 'cpu' | 'memory',
dateString: [string, string]
) {
if (!dateString[0]) {
return;
}
const startTime = parseStrToDate(dateString[0], YYYY_MM_DD_HH_MM_SS);
const endTime = parseStrToDate(dateString[1], YYYY_MM_DD_HH_MM_SS);
getLoad({
type: type,
startTime: startTime.getTime(),
endTime: endTime.getTime(),
}).then(res => {
if (res.code === RESULT_CODE_SUCCESS && res.data) {
if (type === 'load') {
fnLoadChart(res.data.base);
}
if (type === 'cpu') {
fnCPUChart(res.data.base);
}
if (type === 'memory') {
fnMemoryChart(res.data.base);
}
}
});
}
/**查询数据-IO */
function fnGetQueryIO(v: any, dateString: [string, string]) {
if (!v) {
return;
}
const startTime = parseStrToDate(dateString[0], YYYY_MM_DD_HH_MM_SS);
const endTime = parseStrToDate(dateString[1], YYYY_MM_DD_HH_MM_SS);
getLoad({
type: 'io',
startTime: startTime.getTime(),
endTime: endTime.getTime(),
}).then(res => {
if (res.code === RESULT_CODE_SUCCESS && res.data) {
fnIOChart(res.data.io);
}
});
}
/**查询数据-Network */
function fnGetQueryNetwork(v: any, dateString: [string, string]) {
if (!v) {
return;
}
const startTime = parseStrToDate(dateString[0], YYYY_MM_DD_HH_MM_SS);
const endTime = parseStrToDate(dateString[1], YYYY_MM_DD_HH_MM_SS);
getLoad({
type: 'network',
startTime: startTime.getTime(),
endTime: endTime.getTime(),
}).then(res => {
if (res.code === RESULT_CODE_SUCCESS && res.data) {
fnNetworkChart(res.data.network);
}
});
}
onMounted(() => {
fnGetList();
});
</script>
<template>
<PageContainer>
<a-card
:bordered="false"
:body-style="{ marginBottom: '24px', paddingBottom: 0 }"
>
<!-- 表格搜索栏 -->
<a-form name="queryParams" layout="horizontal">
<a-row :gutter="16">
<a-col :lg="8" :md="8" :xs="24">
<a-form-item label="全局过滤" name="neTypeSelect">
<a-range-picker
v-model:value="rangePicker.all"
:allow-clear="false"
bordered
value-format="YYYY-MM-DD HH:mm:ss"
format="YYYY-MM-DD HH:mm:ss"
show-time
:placeholder="['开始时间', '结束时间']"
style="width: 100%"
></a-range-picker>
</a-form-item>
</a-col>
<a-col :lg="6" :md="12" :xs="24">
<a-form-item>
<a-space :size="8">
<a-button type="primary" @click.prevent="fnGetList()">
<template #icon><SearchOutlined /></template>
{{ t('common.search') }}
</a-button>
</a-space>
</a-form-item>
</a-col>
</a-row>
</a-form>
</a-card>
<a-row :gutter="16">
<a-col :lg="24" :md="24" :xs="24">
<a-card :bordered="false" :body-style="{ marginBottom: '24px' }">
<!-- 插槽-卡片左侧侧 -->
<template #title>{{ t('views.monitor.monitor.avgLoad') }}</template>
<!-- 插槽-卡片右侧 -->
<template #extra>
<a-range-picker
v-model:value="rangePicker.load"
:allow-clear="false"
bordered
value-format="YYYY-MM-DD HH:mm:ss"
format="YYYY-MM-DD HH:mm:ss"
show-time
:placeholder="['开始时间', '结束时间']"
style="width: 100%"
@change="(_:any, d:[string,string]) => fnGetQuery('load', d)"
></a-range-picker>
</template>
<div class="chart">
<ChartLine
:option="chartsOption.loadChart"
:dataZoom="true"
height="400px"
></ChartLine>
</div>
</a-card>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<a-card :bordered="false" :body-style="{ marginBottom: '24px' }">
<!-- 插槽-卡片左侧侧 -->
<template #title>CPU</template>
<!-- 插槽-卡片右侧 -->
<template #extra>
<a-range-picker
v-model:value="rangePicker.cpu"
:allow-clear="false"
bordered
value-format="YYYY-MM-DD HH:mm:ss"
format="YYYY-MM-DD HH:mm:ss"
show-time
:placeholder="['开始时间', '结束时间']"
style="width: 100%"
@change="(_:any, d:[string,string]) => fnGetQuery('cpu', d)"
></a-range-picker>
</template>
<div class="chart">
<ChartLine
:option="chartsOption.CPUChart"
:dataZoom="true"
height="400px"
></ChartLine>
</div>
</a-card>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-card :bordered="false" :body-style="{ marginBottom: '24px' }">
<!-- 插槽-卡片左侧侧 -->
<template #title>{{ t('views.monitor.monitor.memory') }}</template>
<!-- 插槽-卡片右侧 -->
<template #extra>
<a-range-picker
v-model:value="rangePicker.memory"
:allow-clear="false"
bordered
value-format="YYYY-MM-DD HH:mm:ss"
format="YYYY-MM-DD HH:mm:ss"
show-time
:placeholder="['开始时间', '结束时间']"
style="width: 100%"
@change="(_:any, d:[string,string]) => fnGetQuery('memory', d)"
></a-range-picker>
</template>
<div class="chart">
<ChartLine
:option="chartsOption.memoryChart"
:dataZoom="true"
height="400px"
></ChartLine>
</div>
</a-card>
</a-col>
</a-row>
<a-row :gutter="16">
<a-col :lg="12" :md="12" :xs="24">
<a-card :bordered="false" :body-style="{ marginBottom: '24px' }">
<!-- 插槽-卡片左侧侧 -->
<template #title>{{ t('views.monitor.monitor.io') }}</template>
<!-- 插槽-卡片右侧 -->
<template #extra>
<a-range-picker
v-model:value="rangePicker.io"
:allow-clear="false"
bordered
value-format="YYYY-MM-DD HH:mm:ss"
format="YYYY-MM-DD HH:mm:ss"
show-time
:placeholder="['开始时间', '结束时间']"
style="width: 100%"
@change="fnGetQueryIO"
></a-range-picker>
</template>
<div class="chart">
<ChartLine
:option="chartsOption.ioChart"
:dataZoom="true"
height="400px"
></ChartLine>
</div>
</a-card>
</a-col>
<a-col :lg="12" :md="12" :xs="24">
<a-card :bordered="false" :body-style="{ marginBottom: '24px' }">
<!-- 插槽-卡片左侧侧 -->
<template #title>{{ t('views.monitor.monitor.network') }}</template>
<!-- 插槽-卡片右侧 -->
<template #extra>
<a-range-picker
v-model:value="rangePicker.network"
:allow-clear="false"
bordered
value-format="YYYY-MM-DD HH:mm:ss"
format="YYYY-MM-DD HH:mm:ss"
show-time
:placeholder="['开始时间', '结束时间']"
style="width: 100%"
@change="fnGetQueryNetwork"
></a-range-picker>
</template>
<div class="chart">
<ChartLine
:option="chartsOption.networkChart"
:dataZoom="true"
height="400px"
></ChartLine>
</div>
</a-card>
</a-col>
</a-row>
</PageContainer>
</template>
<style lang="less" scoped>
.chart {
width: 100%;
height: 400px;
}
</style>