mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-21 17:29:17 +08:00
505 lines
18 KiB
Vue
505 lines
18 KiB
Vue
<template>
|
||
<div>
|
||
<el-row :gutter="20" class="row-box">
|
||
<el-col :span="8">
|
||
<el-card class="el-card">
|
||
<template #header>
|
||
<div class="card-header">
|
||
<span>{{ $t('home.overview') }}</span>
|
||
</div>
|
||
</template>
|
||
<el-row :gutter="20">
|
||
<el-col :span="12">
|
||
<el-card style="font-size: 12px; height: 80px; border-radius: 10px">
|
||
<svg-icon style="float: left; margin-left: 5px" iconName="p-website"></svg-icon>
|
||
<span style="float: left; margin-left: 5px; margin-top: 10px">
|
||
{{ $t('menu.website') }}
|
||
</span>
|
||
<el-link
|
||
style="float: right; font-size: 24px; margin-right: 5px"
|
||
@click="goRouter('/websites')"
|
||
type="primary"
|
||
>
|
||
{{ baseInfo?.websiteNumber }}
|
||
</el-link>
|
||
</el-card>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-card style="font-size: 12px; height: 80px; border-radius: 10px">
|
||
<svg-icon style="float: left; margin-left: 5px" iconName="p-database"></svg-icon>
|
||
<span style="float: left; margin-left: 5px; margin-top: 10px">
|
||
{{ $t('menu.database') }}
|
||
</span>
|
||
<el-link
|
||
style="float: right; font-size: 24px; margin-right: 5px"
|
||
@click="goRouter('/databases')"
|
||
type="primary"
|
||
>
|
||
{{ baseInfo?.databaseNumber }}
|
||
</el-link>
|
||
</el-card>
|
||
</el-col>
|
||
</el-row>
|
||
<el-row :gutter="20" style="margin-top: 20px; margin-top: 30px">
|
||
<el-col :span="12">
|
||
<el-card style="font-size: 12px; height: 80px; border-radius: 10px">
|
||
<svg-icon style="float: left; margin-left: 5px" iconName="p-plan"></svg-icon>
|
||
<span style="float: left; margin-left: 5px; margin-top: 10px">
|
||
{{ $t('menu.cronjob') }}
|
||
</span>
|
||
<el-link
|
||
style="float: right; font-size: 24px; margin-right: 5px"
|
||
@click="goRouter('/cronjobs')"
|
||
type="primary"
|
||
>
|
||
{{ baseInfo?.cronjobNumber }}
|
||
</el-link>
|
||
</el-card>
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-card style="font-size: 12px; height: 80px; border-radius: 10px">
|
||
<svg-icon style="float: left; margin-left: 5px" iconName="p-appstore"></svg-icon>
|
||
<span style="float: left; margin-left: 5px; margin-top: 10px">
|
||
{{ $t('home.appInstalled') }}
|
||
</span>
|
||
<el-link
|
||
style="float: right; font-size: 24px; margin-right: 5px"
|
||
@click="goRouter('/apps')"
|
||
type="primary"
|
||
>
|
||
{{ baseInfo?.appInstalldNumber }}
|
||
</el-link>
|
||
</el-card>
|
||
</el-col>
|
||
</el-row>
|
||
</el-card>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-card class="el-card">
|
||
<template #header>
|
||
<div class="card-header">
|
||
<span>{{ $t('commons.table.status') }}</span>
|
||
</div>
|
||
</template>
|
||
<Status ref="statuRef" />
|
||
</el-card>
|
||
</el-col>
|
||
<el-col :span="8">
|
||
<el-card class="el-card">
|
||
<template #header>
|
||
<div class="card-header">
|
||
<span>{{ $t('home.systemInfo') }}</span>
|
||
</div>
|
||
</template>
|
||
<el-form>
|
||
<el-form-item :label="$t('home.hostname')">{{ baseInfo.hostname }}</el-form-item>
|
||
<el-form-item :label="$t('home.platformVersion')">
|
||
{{ baseInfo.platform }}-{{ baseInfo.platformVersion }}
|
||
</el-form-item>
|
||
<el-form-item :label="$t('home.kernelVersion')">
|
||
{{ baseInfo.kernelVersion }}
|
||
</el-form-item>
|
||
<el-form-item :label="$t('home.kernelArch')">{{ baseInfo.kernelArch }}</el-form-item>
|
||
<el-form-item :label="$t('home.uptime')">{{ baseInfo.uptime }}</el-form-item>
|
||
<el-form-item :label="$t('home.runningTime')">{{ baseInfo.timeSinceUptime }}</el-form-item>
|
||
</el-form>
|
||
</el-card>
|
||
</el-col>
|
||
</el-row>
|
||
<el-row :gutter="20" style="margin-top: 20px" class="row-box">
|
||
<el-col :span="12">
|
||
<App ref="appRef" />
|
||
</el-col>
|
||
<el-col :span="12">
|
||
<el-card class="el-card">
|
||
<el-radio-group v-model="chartOption" @change="changeOption">
|
||
<el-radio-button label="network">{{ $t('home.network') }}</el-radio-button>
|
||
<el-radio-button label="io">{{ $t('home.io') }}</el-radio-button>
|
||
</el-radio-group>
|
||
<el-select
|
||
v-if="chartOption === 'network'"
|
||
@change="onLoadBaseInfo(false, 'network')"
|
||
v-model="searchInfo.netOption"
|
||
style="float: right"
|
||
>
|
||
<el-option v-for="item in netOptions" :key="item" :label="item" :value="item" />
|
||
</el-select>
|
||
<el-select
|
||
v-if="chartOption === 'io'"
|
||
v-model="searchInfo.ioOption"
|
||
@change="onLoadBaseInfo(false, 'io')"
|
||
style="float: right"
|
||
>
|
||
<el-option v-for="item in ioOptions" :key="item" :label="item" :value="item" />
|
||
</el-select>
|
||
<div style="margin-top: 20px" v-if="chartOption === 'network'">
|
||
<el-tag>{{ $t('monitor.up') }}: {{ currentChartInfo.netBytesSent }} KB/s</el-tag>
|
||
<el-tag style="margin-left: 20px">
|
||
{{ $t('monitor.down') }}: {{ currentChartInfo.netBytesRecv }} KB/s
|
||
</el-tag>
|
||
<el-tag style="margin-left: 20px">
|
||
{{ $t('home.totalSend') }}: {{ computeSize(currentInfo.netBytesSent) }}
|
||
</el-tag>
|
||
<el-tag style="margin-left: 20px">
|
||
{{ $t('home.totalRecv') }}: {{ computeSize(currentInfo.netBytesRecv) }}
|
||
</el-tag>
|
||
</div>
|
||
<div style="margin-top: 20px" v-if="chartOption === 'io'">
|
||
<el-tag>{{ $t('monitor.read') }}: {{ currentChartInfo.ioReadBytes }} MB</el-tag>
|
||
<el-tag style="margin-left: 20px">
|
||
{{ $t('monitor.write') }}: {{ currentChartInfo.ioWriteBytes }} MB
|
||
</el-tag>
|
||
<el-tag style="margin-left: 20px">
|
||
{{ $t('home.rwPerSecond') }}: {{ currentChartInfo.ioCount }} {{ $t('home.time') }}
|
||
</el-tag>
|
||
<el-tag style="margin-left: 20px">
|
||
{{ $t('home.rwPerSecond') }}: {{ currentInfo.ioTime }} ms
|
||
</el-tag>
|
||
</div>
|
||
<div
|
||
v-if="chartOption === 'io'"
|
||
id="ioChart"
|
||
style="margin-top: 20px; width: 100%; height: 320px"
|
||
></div>
|
||
<div
|
||
v-if="chartOption === 'network'"
|
||
id="networkChart"
|
||
style="margin-top: 20px; width: 100%; height: 320px"
|
||
></div>
|
||
</el-card>
|
||
</el-col>
|
||
</el-row>
|
||
</div>
|
||
</template>
|
||
|
||
<script lang="ts" setup>
|
||
import { onMounted, onBeforeUnmount, ref, reactive } from 'vue';
|
||
import * as echarts from 'echarts';
|
||
import Status from '@/views/home/status/index.vue';
|
||
import App from '@/views/home/app/index.vue';
|
||
import i18n from '@/lang';
|
||
import { Dashboard } from '@/api/interface/dashboard';
|
||
import { dateFromatForSecond, computeSize } from '@/utils/util';
|
||
import { useRouter } from 'vue-router';
|
||
import { loadBaseInfo, loadCurrentInfo } from '@/api/modules/dashboard';
|
||
import { getIOOptions, getNetworkOptions } from '@/api/modules/monitor';
|
||
const router = useRouter();
|
||
|
||
const statuRef = ref();
|
||
const appRef = ref();
|
||
|
||
const chartOption = ref('network');
|
||
let timer: NodeJS.Timer | null = null;
|
||
let isInit = ref<boolean>(true);
|
||
|
||
const ioReadBytes = ref<Array<number>>([]);
|
||
const ioWriteBytes = ref<Array<number>>([]);
|
||
const netBytesSents = ref<Array<number>>([]);
|
||
const netBytesRecvs = ref<Array<number>>([]);
|
||
const timeIODatas = ref<Array<string>>([]);
|
||
const timeNetDatas = ref<Array<string>>([]);
|
||
|
||
const ioOptions = ref();
|
||
const netOptions = ref();
|
||
const searchInfo = reactive({
|
||
ioOption: 'all',
|
||
netOption: 'all',
|
||
});
|
||
|
||
const baseInfo = ref<Dashboard.BaseInfo>({
|
||
haloID: 0,
|
||
dateeaseID: 0,
|
||
jumpserverID: 0,
|
||
metersphereID: 0,
|
||
kubeoperatorID: 0,
|
||
kubepiID: 0,
|
||
|
||
websiteNumber: 0,
|
||
databaseNumber: 0,
|
||
cronjobNumber: 0,
|
||
appInstalldNumber: 0,
|
||
|
||
hostname: '',
|
||
os: '',
|
||
platform: '',
|
||
platformFamily: '',
|
||
platformVersion: '',
|
||
kernelArch: '',
|
||
kernelVersion: '',
|
||
virtualizationSystem: '',
|
||
uptime: '',
|
||
timeSinceUptime: '',
|
||
|
||
cpuCores: 0,
|
||
cpuLogicalCores: 0,
|
||
cpuModelName: '',
|
||
currentInfo: null,
|
||
});
|
||
const currentInfo = ref<Dashboard.CurrentInfo>({
|
||
procs: 0,
|
||
|
||
load1: 0,
|
||
load5: 0,
|
||
load15: 0,
|
||
loadUsagePercent: 0,
|
||
|
||
cpuPercent: [] as Array<number>,
|
||
cpuUsedPercent: 0,
|
||
cpuUsed: 0,
|
||
cpuTotal: 0,
|
||
|
||
memoryTotal: 0,
|
||
memoryAvailable: 0,
|
||
memoryUsed: 0,
|
||
MemoryUsedPercent: 0,
|
||
|
||
ioReadBytes: 0,
|
||
ioWriteBytes: 0,
|
||
ioTime: 0,
|
||
ioCount: 0,
|
||
|
||
total: 0,
|
||
free: 0,
|
||
used: 0,
|
||
usedPercent: 0,
|
||
|
||
inodesTotal: 0,
|
||
inodesUsed: 0,
|
||
inodesFree: 0,
|
||
inodesUsedPercent: 0,
|
||
|
||
netBytesSent: 0,
|
||
netBytesRecv: 0,
|
||
|
||
shotTime: new Date(),
|
||
});
|
||
const currentChartInfo = reactive({
|
||
ioReadBytes: 0,
|
||
ioWriteBytes: 0,
|
||
ioCount: 0,
|
||
|
||
netBytesSent: 0,
|
||
netBytesRecv: 0,
|
||
});
|
||
|
||
const changeOption = async () => {
|
||
isInit.value = true;
|
||
loadData();
|
||
};
|
||
|
||
const goRouter = async (path: string) => {
|
||
router.push({ path: path });
|
||
};
|
||
|
||
const onLoadNetworkOptions = async () => {
|
||
const res = await getNetworkOptions();
|
||
netOptions.value = res.data;
|
||
searchInfo.netOption = netOptions.value && netOptions.value[0];
|
||
};
|
||
|
||
const onLoadIOOptions = async () => {
|
||
const res = await getIOOptions();
|
||
ioOptions.value = res.data;
|
||
searchInfo.ioOption = ioOptions.value && ioOptions.value[0];
|
||
};
|
||
|
||
const onLoadBaseInfo = async (isInit: boolean, range: string) => {
|
||
if (range === 'all' || range === 'io') {
|
||
ioReadBytes.value = [];
|
||
ioWriteBytes.value = [];
|
||
timeIODatas.value = [];
|
||
} else if (range === 'all' || range === 'network') {
|
||
netBytesSents.value = [];
|
||
netBytesRecvs.value = [];
|
||
timeNetDatas.value = [];
|
||
}
|
||
const res = await loadBaseInfo(searchInfo.ioOption, searchInfo.netOption);
|
||
baseInfo.value = res.data;
|
||
currentInfo.value = baseInfo.value.currentInfo;
|
||
if (baseInfo.value.timeSinceUptime) {
|
||
baseInfo.value.timeSinceUptime.replaceAll('days', i18n.global.t('home.Day'));
|
||
baseInfo.value.timeSinceUptime.replaceAll('hours', i18n.global.t('home.Hour'));
|
||
baseInfo.value.timeSinceUptime.replaceAll('minutes', i18n.global.t('home.Minute'));
|
||
}
|
||
onLoadCurrentInfo();
|
||
statuRef.value.acceptParams(currentInfo.value, baseInfo.value);
|
||
appRef.value.acceptParams(baseInfo.value);
|
||
if (isInit) {
|
||
window.addEventListener('resize', changeChartSize);
|
||
timer = setInterval(async () => {
|
||
onLoadCurrentInfo();
|
||
}, 3000);
|
||
}
|
||
};
|
||
|
||
const onLoadCurrentInfo = async () => {
|
||
const res = await loadCurrentInfo(searchInfo.ioOption, searchInfo.netOption);
|
||
currentChartInfo.netBytesSent = Number(
|
||
((res.data.netBytesSent - currentInfo.value.netBytesSent) / 1024 / 3).toFixed(2),
|
||
);
|
||
netBytesSents.value.push(currentChartInfo.netBytesSent);
|
||
if (netBytesSents.value.length > 20) {
|
||
netBytesSents.value.splice(0, 1);
|
||
}
|
||
currentChartInfo.netBytesRecv = Number(
|
||
((res.data.netBytesRecv - currentInfo.value.netBytesRecv) / 1024 / 3).toFixed(2),
|
||
);
|
||
netBytesRecvs.value.push(currentChartInfo.netBytesRecv);
|
||
if (netBytesRecvs.value.length > 20) {
|
||
netBytesRecvs.value.splice(0, 1);
|
||
}
|
||
|
||
currentChartInfo.ioReadBytes = Number(
|
||
((res.data.ioReadBytes - currentInfo.value.ioReadBytes) / 1024 / 1024 / 3).toFixed(2),
|
||
);
|
||
ioReadBytes.value.push(currentChartInfo.ioReadBytes);
|
||
if (ioReadBytes.value.length > 20) {
|
||
ioReadBytes.value.splice(0, 1);
|
||
}
|
||
currentChartInfo.ioWriteBytes = Number(
|
||
((res.data.ioWriteBytes - currentInfo.value.ioWriteBytes) / 1024 / 1024 / 3).toFixed(2),
|
||
);
|
||
ioWriteBytes.value.push(currentChartInfo.ioWriteBytes);
|
||
if (ioWriteBytes.value.length > 20) {
|
||
ioWriteBytes.value.splice(0, 1);
|
||
}
|
||
currentChartInfo.ioCount = Number(((res.data.ioCount - currentInfo.value.ioCount) / 3).toFixed(2));
|
||
|
||
timeIODatas.value.push(dateFromatForSecond(res.data.shotTime));
|
||
if (timeIODatas.value.length > 20) {
|
||
timeIODatas.value.splice(0, 1);
|
||
}
|
||
timeNetDatas.value.push(dateFromatForSecond(res.data.shotTime));
|
||
if (timeNetDatas.value.length > 20) {
|
||
timeNetDatas.value.splice(0, 1);
|
||
}
|
||
loadData();
|
||
currentInfo.value = res.data;
|
||
statuRef.value.acceptParams(currentInfo.value, baseInfo.value);
|
||
};
|
||
|
||
const loadData = async () => {
|
||
if (chartOption.value === 'io') {
|
||
let ioReadYDatas = {
|
||
name: i18n.global.t('monitor.read'),
|
||
type: 'line',
|
||
smooth: true,
|
||
areaStyle: {
|
||
color: '#ebdee3',
|
||
},
|
||
data: ioReadBytes.value,
|
||
showSymbol: false,
|
||
};
|
||
let ioWriteYDatas = {
|
||
name: i18n.global.t('monitor.write'),
|
||
type: 'line',
|
||
smooth: true,
|
||
areaStyle: {
|
||
color: '#ebdee3',
|
||
},
|
||
data: ioWriteBytes.value,
|
||
showSymbol: false,
|
||
};
|
||
freshChart(
|
||
'ioChart',
|
||
[i18n.global.t('monitor.read'), i18n.global.t('monitor.write')],
|
||
timeIODatas.value,
|
||
[ioReadYDatas, ioWriteYDatas],
|
||
i18n.global.t('monitor.network'),
|
||
'MB',
|
||
);
|
||
} else {
|
||
let netTxYDatas = {
|
||
name: i18n.global.t('monitor.up'),
|
||
type: 'line',
|
||
smooth: true,
|
||
areaStyle: {
|
||
color: '#ebdee3',
|
||
},
|
||
data: netBytesRecvs.value,
|
||
showSymbol: false,
|
||
};
|
||
let netRxYDatas = {
|
||
name: i18n.global.t('monitor.down'),
|
||
type: 'line',
|
||
smooth: true,
|
||
areaStyle: {
|
||
color: '#ebdee3',
|
||
},
|
||
data: netBytesSents.value,
|
||
showSymbol: false,
|
||
};
|
||
freshChart(
|
||
'networkChart',
|
||
[i18n.global.t('monitor.up'), i18n.global.t('monitor.down')],
|
||
timeNetDatas.value,
|
||
[netTxYDatas, netRxYDatas],
|
||
i18n.global.t('monitor.network'),
|
||
'KB/s',
|
||
);
|
||
}
|
||
};
|
||
|
||
function freshChart(chartName: string, legendDatas: any, xDatas: any, yDatas: any, yTitle: string, formatStr: string) {
|
||
if (isInit.value) {
|
||
echarts.init(document.getElementById(chartName) as HTMLElement);
|
||
isInit.value = false;
|
||
}
|
||
let itemChart = echarts.getInstanceByDom(document.getElementById(chartName) as HTMLElement);
|
||
const option = {
|
||
title: [
|
||
{
|
||
left: 'center',
|
||
text: yTitle,
|
||
},
|
||
],
|
||
zlevel: 1,
|
||
z: 1,
|
||
tooltip: {
|
||
trigger: 'axis',
|
||
formatter: function (datas: any) {
|
||
let res = datas[0].name + '<br/>';
|
||
for (const item of datas) {
|
||
res += item.marker + ' ' + item.seriesName + ':' + item.data + formatStr + '<br/>';
|
||
}
|
||
return res;
|
||
},
|
||
},
|
||
grid: { left: '7%', right: '7%', bottom: '20%' },
|
||
legend: {
|
||
data: legendDatas,
|
||
right: 10,
|
||
},
|
||
xAxis: { data: xDatas, boundaryGap: false },
|
||
yAxis: { name: '( ' + formatStr + ' )' },
|
||
series: yDatas,
|
||
};
|
||
itemChart?.setOption(option, true);
|
||
}
|
||
|
||
function changeChartSize() {
|
||
echarts.getInstanceByDom(document.getElementById('ioChart') as HTMLElement)?.resize();
|
||
echarts.getInstanceByDom(document.getElementById('networkChart') as HTMLElement)?.resize();
|
||
}
|
||
|
||
onMounted(() => {
|
||
onLoadNetworkOptions();
|
||
onLoadIOOptions();
|
||
onLoadBaseInfo(true, 'all');
|
||
});
|
||
|
||
onBeforeUnmount(() => {
|
||
clearInterval(Number(timer));
|
||
timer = null;
|
||
window.removeEventListener('resize', changeChartSize);
|
||
});
|
||
</script>
|
||
|
||
<style scoped>
|
||
.card-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
</style>
|