feat: async load data of top CPU and memory processes data (#11081)

* feat: async load data of top CPU and memory processes data

* refactor: Update CPU and memory top toggle functionality and state management

---------

Co-authored-by: 王贺 <wanghe@fit2cloud.com>
This commit is contained in:
KOMATA
2025-11-26 23:02:09 +08:00
committed by GitHub
co-authored by 王贺
parent 8e03b24b0c
commit 747a0c45ee
7 changed files with 181 additions and 24 deletions
+22
View File
@@ -179,6 +179,28 @@ func (b *BaseApi) LoadDashboardCurrentInfo(c *gin.Context) {
helper.SuccessWithData(c, data)
}
// @Tags Dashboard
// @Summary Load top cpu processes
// @Success 200 {Array} dto.Process
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /dashboard/current/top/cpu [get]
func (b *BaseApi) LoadDashboardTopCPU(c *gin.Context) {
data := dashboardService.LoadTopCPU()
helper.SuccessWithData(c, data)
}
// @Tags Dashboard
// @Summary Load top memory processes
// @Success 200 {Array} dto.Process
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /dashboard/current/top/mem [get]
func (b *BaseApi) LoadDashboardTopMem(c *gin.Context) {
data := dashboardService.LoadTopMem()
helper.SuccessWithData(c, data)
}
// @Tags Dashboard
// @Summary System restart
// @Accept json
+10 -3
View File
@@ -39,6 +39,8 @@ type IDashboardService interface {
LoadBaseInfo(ioOption string, netOption string) (*dto.DashboardBase, error)
LoadCurrentInfoForNode() *dto.NodeCurrent
LoadCurrentInfo(ioOption string, netOption string) *dto.DashboardCurrent
LoadTopCPU() []dto.Process
LoadTopMem() []dto.Process
LoadQuickOptions() []dto.QuickJump
ChangeQuick(req dto.ChangeQuicks) error
@@ -217,9 +219,6 @@ func (u *DashboardService) LoadCurrentInfo(ioOption string, netOption string) *d
currentInfo.GPUData = loadGPUInfo()
currentInfo.XPUData = loadXpuInfo()
currentInfo.TopCPUItems = loadTopCPU()
currentInfo.TopMemItems = loadTopMem()
if ioOption == "all" {
diskInfo, _ := disk.IOCounters()
for _, state := range diskInfo {
@@ -261,6 +260,14 @@ func (u *DashboardService) LoadCurrentInfo(ioOption string, netOption string) *d
return &currentInfo
}
func (u *DashboardService) LoadTopCPU() []dto.Process {
return loadTopCPU()
}
func (u *DashboardService) LoadTopMem() []dto.Process {
return loadTopMem()
}
func (u *DashboardService) LoadAppLauncher(ctx *gin.Context) ([]dto.AppLauncher, error) {
var data []dto.AppLauncher
appInstalls, err := appInstallRepo.ListBy(context.Background())
+2
View File
@@ -20,6 +20,8 @@ func (s *DashboardRouter) InitRouter(Router *gin.RouterGroup) {
cmdRouter.GET("/base/:ioOption/:netOption", baseApi.LoadDashboardBaseInfo)
cmdRouter.GET("/current/node", baseApi.LoadCurrentInfoForNode)
cmdRouter.GET("/current/:ioOption/:netOption", baseApi.LoadDashboardCurrentInfo)
cmdRouter.GET("/current/top/cpu", baseApi.LoadDashboardTopCPU)
cmdRouter.GET("/current/top/mem", baseApi.LoadDashboardTopMem)
cmdRouter.POST("/system/restart/:operation", baseApi.SystemRestart)
}
}
+2 -2
View File
@@ -103,8 +103,8 @@ export namespace Dashboard {
gpuData: Array<GPUInfo>;
xpuData: Array<XPUInfo>;
topCPUItems: Array<Process>;
topMemItems: Array<Process>;
topCPUItems?: Array<Process>;
topMemItems?: Array<Process>;
netBytesSent: number;
netBytesRecv: number;
+8
View File
@@ -29,6 +29,14 @@ export const loadCurrentInfo = (ioOption: string, netOption: string) => {
return http.get<Dashboard.CurrentInfo>(`/dashboard/current/${ioOption}/${netOption}`);
};
export const loadTopCPU = () => {
return http.get<Array<Dashboard.Process>>(`/dashboard/current/top/cpu`);
};
export const loadTopMem = () => {
return http.get<Array<Dashboard.Process>>(`/dashboard/current/top/mem`);
};
export const systemRestart = (operation: string) => {
return http.post(`/dashboard/system/restart/${operation}`);
};
+10 -2
View File
@@ -450,6 +450,14 @@ const currentChartInfo = reactive({
const chartsOption = ref({ ioChart1: null, networkChart: null });
const updateCurrentInfo = (data: Dashboard.CurrentInfo) => {
currentInfo.value = {
...data,
topCPUItems: currentInfo.value.topCPUItems || [],
topMemItems: currentInfo.value.topMemItems || [],
};
};
const changeOption = async () => {
isInit.value = true;
loadData();
@@ -484,7 +492,7 @@ const onLoadBaseInfo = async (isInit: boolean, range: string) => {
}
const res = await loadBaseInfo(searchInfo.ioOption, searchInfo.netOption);
baseInfo.value = res.data;
currentInfo.value = baseInfo.value.currentInfo;
updateCurrentInfo(baseInfo.value.currentInfo);
onLoadCurrentInfo();
isStatusInit.value = false;
statusRef.value?.acceptParams(currentInfo.value, baseInfo.value);
@@ -584,7 +592,7 @@ const onLoadCurrentInfo = async () => {
timeNetDatas.value.splice(0, 1);
}
loadData();
currentInfo.value = res.data;
updateCurrentInfo(res.data);
statusRef.value?.acceptParams(currentInfo.value, baseInfo.value);
};
+127 -17
View File
@@ -1,7 +1,13 @@
<template>
<div class="custom-row">
<el-col :xs="6" :sm="6" :md="3" :lg="3" :xl="3" align="center">
<el-popover :hide-after="20" :teleported="false" :width="320" v-if="chartsOption['load']">
<el-popover
:hide-after="20"
:teleported="false"
:width="320"
v-if="chartsOption['load']"
@hide="onCpuPopoverHide"
>
<el-descriptions :column="1" size="small">
<el-descriptions-item :label="$t('home.loadAverage', [1])">
{{ formatNumber(currentInfo.load1) }}
@@ -14,12 +20,12 @@
</el-descriptions-item>
</el-descriptions>
<el-button link size="small" type="primary" class="float-left mb-2" @click="showTop = !showTop">
<el-button link size="small" type="primary" class="float-left mb-2" @click="toggleCpuTop">
{{ $t('home.cpuTop') }}
<el-icon v-if="!showTop"><ArrowRight /></el-icon>
<el-icon v-if="showTop"><ArrowDown /></el-icon>
<el-icon v-if="!showCpuTop"><ArrowRight /></el-icon>
<el-icon v-if="showCpuTop"><ArrowDown /></el-icon>
</el-button>
<ComplexTable v-if="showTop" :data="currentInfo.topCPUItems">
<ComplexTable v-if="showCpuTop" :data="currentInfo.topCPUItems">
<el-table-column :min-width="120" show-overflow-tooltip :label="$t('menu.process')" prop="name" />
<el-table-column :min-width="60" :label="$t('monitor.percent')" prop="percent">
<template #default="{ row }">{{ row.percent.toFixed(2) }}%</template>
@@ -45,7 +51,13 @@
<span class="input-help">{{ loadStatus(currentInfo.loadUsagePercent) }}</span>
</el-col>
<el-col :xs="6" :sm="6" :md="3" :lg="3" :xl="3">
<el-popover :hide-after="20" :teleported="false" :width="430" v-if="chartsOption['cpu']">
<el-popover
:hide-after="20"
:teleported="false"
:width="430"
v-if="chartsOption['cpu']"
@hide="onCpuPopoverHide"
>
<el-descriptions :title="baseInfo.cpuModelName" :column="2" size="small">
<el-descriptions-item :label="$t('home.core')">
{{ baseInfo.cpuCores }}
@@ -71,12 +83,12 @@
</div>
<br />
<el-button link size="small" type="primary" class="mt-1 mb-2" @click="showTop = !showTop">
<el-button link size="small" type="primary" class="mt-2 mb-2" @click="toggleCpuTop">
{{ $t('home.cpuTop') }}
<el-icon v-if="!showTop"><ArrowRight /></el-icon>
<el-icon v-if="showTop"><ArrowDown /></el-icon>
<el-icon v-if="!showCpuTop"><ArrowRight /></el-icon>
<el-icon v-if="showCpuTop"><ArrowDown /></el-icon>
</el-button>
<ComplexTable v-if="showTop" :data="currentInfo.topCPUItems">
<ComplexTable v-if="showCpuTop" :data="currentInfo.topCPUItems">
<el-table-column :min-width="120" show-overflow-tooltip :label="$t('menu.process')" prop="name" />
<el-table-column :min-width="60" :label="$t('monitor.percent')" prop="percent">
<template #default="{ row }">{{ row.percent.toFixed(2) }}%</template>
@@ -107,7 +119,13 @@
</div>
</el-col>
<el-col :xs="6" :sm="6" :md="3" :lg="3" :xl="3" align="center">
<el-popover :hide-after="20" :teleported="false" :width="480" v-if="chartsOption['memory']">
<el-popover
:hide-after="20"
:teleported="false"
:width="480"
v-if="chartsOption['memory']"
@hide="onMemPopoverHide"
>
<el-descriptions direction="vertical" :title="$t('home.mem')" class="ml-1" :column="4" size="small">
<el-descriptions-item :label-width="60" :label="$t('home.total')">
{{ computeSize(currentInfo.memoryTotal) }}
@@ -154,12 +172,12 @@
</el-descriptions-item>
</el-descriptions>
<el-button link size="small" type="primary" class="float-left mb-2" @click="showTop = !showTop">
<el-button link size="small" type="primary" class="float-left mb-2" @click="toggleMemTop">
{{ $t('home.memTop') }}
<el-icon v-if="!showTop"><ArrowRight /></el-icon>
<el-icon v-if="showTop"><ArrowDown /></el-icon>
<el-icon v-if="!showMemTop"><ArrowRight /></el-icon>
<el-icon v-if="showMemTop"><ArrowDown /></el-icon>
</el-button>
<ComplexTable v-if="showTop" :data="currentInfo.topMemItems">
<ComplexTable v-if="showMemTop" :data="currentInfo.topMemItems">
<el-table-column :min-width="120" show-overflow-tooltip :label="$t('menu.process')" prop="name" />
<el-table-column :min-width="100" :label="$t('monitor.memory')" prop="memory">
<template #default="{ row }">
@@ -334,13 +352,19 @@
import { Dashboard } from '@/api/interface/dashboard';
import { computeSize } from '@/utils/util';
import i18n from '@/lang';
import { nextTick, ref } from 'vue';
import { nextTick, onBeforeUnmount, ref } from 'vue';
import { routerToFileWithPath, routerToName } from '@/utils/router';
import { stopProcess } from '@/api/modules/process';
import { loadTopCPU, loadTopMem } from '@/api/modules/dashboard';
import { MsgSuccess } from '@/utils/message';
const showMore = ref(false);
const totalCount = ref();
let cpuPopoverTimer: ReturnType<typeof setTimeout> | null = null;
let memPopoverTimer: ReturnType<typeof setTimeout> | null = null;
let cpuLoading = false;
let memLoading = false;
const baseInfo = ref<Dashboard.BaseInfo>({
hostname: '',
os: '',
@@ -405,7 +429,8 @@ const currentInfo = ref<Dashboard.CurrentInfo>({
});
const cpuShowAll = ref();
const showTop = ref();
const showCpuTop = ref(false);
const showMemTop = ref(false);
const killProcessID = ref();
const confirmConfRef = ref();
@@ -515,6 +540,91 @@ function formatNumber(val: number) {
return Number(val.toFixed(2));
}
const toggleCpuTop = async () => {
showCpuTop.value = !showCpuTop.value;
if (showCpuTop.value) {
await loadTopCPUData();
if (cpuPopoverTimer) {
clearInterval(Number(cpuPopoverTimer));
}
cpuPopoverTimer = setInterval(loadTopCPUData, 5000);
} else {
if (cpuPopoverTimer) {
clearInterval(Number(cpuPopoverTimer));
cpuPopoverTimer = null;
}
}
};
const onCpuPopoverHide = () => {
showCpuTop.value = false;
if (cpuPopoverTimer) {
clearInterval(Number(cpuPopoverTimer));
cpuPopoverTimer = null;
}
};
const toggleMemTop = async () => {
showMemTop.value = !showMemTop.value;
if (showMemTop.value) {
await loadTopMemData();
if (memPopoverTimer) {
clearInterval(Number(memPopoverTimer));
}
memPopoverTimer = setInterval(loadTopMemData, 5000);
} else {
if (memPopoverTimer) {
clearInterval(Number(memPopoverTimer));
memPopoverTimer = null;
}
}
};
const onMemPopoverHide = () => {
showMemTop.value = false;
if (memPopoverTimer) {
clearInterval(Number(memPopoverTimer));
memPopoverTimer = null;
}
};
const loadTopCPUData = async () => {
if (cpuLoading) return;
cpuLoading = true;
try {
const res = await loadTopCPU();
currentInfo.value.topCPUItems = res.data || [];
} catch (_error) {
// ignore load errors
} finally {
cpuLoading = false;
}
};
const loadTopMemData = async () => {
if (memLoading) return;
memLoading = true;
try {
const res = await loadTopMem();
currentInfo.value.topMemItems = res.data || [];
} catch (_error) {
// ignore load errors
} finally {
memLoading = false;
}
};
onBeforeUnmount(() => {
if (cpuPopoverTimer) {
clearInterval(Number(cpuPopoverTimer));
cpuPopoverTimer = null;
}
if (memPopoverTimer) {
clearInterval(Number(memPopoverTimer));
memPopoverTimer = null;
}
});
defineExpose({
acceptParams,
});