feat: Add real-time process display on the overview page (#10833)

This commit is contained in:
ssongliu 2025-10-31 18:36:19 +08:00 committed by GitHub
parent 18ab07ec4f
commit eaa620b081
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 395 additions and 230 deletions

View file

@ -116,6 +116,9 @@ type DashboardCurrent struct {
GPUData []GPUInfo `json:"gpuData"`
XPUData []XPUInfo `json:"xpuData"`
TopCPUItems []Process `json:"topCPUItems"`
TopMemItems []Process `json:"topMemItems"`
ShotTime time.Time `json:"shotTime"`
}

View file

@ -120,7 +120,7 @@ func (u *DashboardService) LoadCurrentInfoForNode() *dto.NodeCurrent {
memoryInfo, _ := mem.VirtualMemory()
currentInfo.MemoryTotal = memoryInfo.Total
currentInfo.MemoryAvailable = memoryInfo.Available
currentInfo.MemoryUsed = memoryInfo.Used + memoryInfo.Shared
currentInfo.MemoryUsed = memoryInfo.Used
currentInfo.MemoryUsedPercent = memoryInfo.UsedPercent
swapInfo, _ := mem.SwapMemory()
@ -194,7 +194,7 @@ func (u *DashboardService) LoadCurrentInfo(ioOption string, netOption string) *d
memoryInfo, _ := mem.VirtualMemory()
currentInfo.MemoryTotal = memoryInfo.Total
currentInfo.MemoryUsed = memoryInfo.Used + memoryInfo.Shared
currentInfo.MemoryUsed = memoryInfo.Used
currentInfo.MemoryFree = memoryInfo.Free
currentInfo.MemoryCache = memoryInfo.Cached + memoryInfo.Buffers
currentInfo.MemoryShard = memoryInfo.Shared
@ -211,6 +211,9 @@ 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 {

View file

@ -183,7 +183,7 @@ func (m *MonitorService) Run() {
if len(totalPercent) == 1 {
itemModel.Cpu = totalPercent[0]
}
topCPU := m.loadTopCPU()
topCPU := loadTopCPU()
if len(topCPU) != 0 {
topItemCPU, err := json.Marshal(topCPU)
if err == nil {
@ -199,7 +199,7 @@ func (m *MonitorService) Run() {
memoryInfo, _ := mem.VirtualMemory()
itemModel.Memory = memoryInfo.UsedPercent
topMem := m.loadTopMem()
topMem := loadTopMem()
if len(topMem) != 0 {
topMemItem, err := json.Marshal(topMem)
if err == nil {
@ -350,7 +350,7 @@ func (m *MonitorService) saveNetDataToDB(ctx context.Context, interval float64)
}
}
func (m *MonitorService) loadTopCPU() []dto.Process {
func loadTopCPU() []dto.Process {
processes, err := process.Processes()
if err != nil {
return nil
@ -400,7 +400,7 @@ func (m *MonitorService) loadTopCPU() []dto.Process {
return top5
}
func (m *MonitorService) loadTopMem() []dto.Process {
func loadTopMem() []dto.Process {
processes, err := process.Processes()
if err != nil {
return nil

View file

@ -103,11 +103,22 @@ export namespace Dashboard {
gpuData: Array<GPUInfo>;
xpuData: Array<XPUInfo>;
topCPUItems: Array<Process>;
topMemItems: Array<Process>;
netBytesSent: number;
netBytesRecv: number;
shotTime: Date;
}
export interface Process {
name: string;
pid: number;
percent: number;
memory: number;
cmd: string;
user: string;
}
export interface DiskInfo {
path: string;
type: string;

View file

@ -426,7 +426,7 @@ const message = {
ioDelay: 'I/O latency',
uptime: 'Uptime',
runningTime: 'Up since',
mem: 'System',
mem: 'System Memory',
swapMem: 'Swap Partition',
runSmoothly: 'Low load',
@ -436,6 +436,9 @@ const message = {
core: 'Physical core',
logicCore: 'Logical core',
corePercent: 'Core Usage',
cpuTop: 'Top 5 Processes by CPU Usage',
memTop: 'Top 5 Processes by Memory Usage',
loadAverage: 'Load average in the last 1 minute | Load average in the last {n} minutes',
load: 'Load',
mount: 'Mount point',
@ -3009,6 +3012,9 @@ const message = {
stopProcess: 'End',
viewDetails: 'View details',
stopProcessWarn: 'Are you sure you want to end this process (PID:{0})?',
kill: 'Kill Process',
killNow: 'Kill Now',
killHelper: 'Killing process {0} may cause some programs to malfunction. Continue?',
processName: 'Process name',
},
tool: {

View file

@ -434,7 +434,7 @@ const message = {
ioDelay: 'Latencia de E/S',
uptime: 'Tiempo en funcionamiento',
runningTime: 'Desde',
mem: 'Sistema',
mem: 'Memoria del Sistema',
swapMem: 'Partición swap',
runSmoothly: 'Carga baja',
runNormal: 'Carga moderada',
@ -442,6 +442,9 @@ const message = {
runJam: 'Carga pesada',
core: 'Núcleo físico',
logicCore: 'Núcleo lógico',
corePercent: 'Uso del Núcleo',
cpuTop: 'Top 5 de Procesos por Uso de CPU',
memTop: 'Top 5 de Procesos por Uso de Memoria',
loadAverage: 'Promedio de carga en el último minuto | Promedio de carga en los últimos {n} minutos',
load: 'Carga',
mount: 'Punto de montaje',
@ -2982,6 +2985,9 @@ const message = {
stopProcess: 'Finalizar',
viewDetails: 'Ver detalles',
stopProcessWarn: '¿Seguro que deseas finalizar este proceso (PID:{0})?',
kill: 'Terminar Proceso',
killNow: 'Terminar Ahora',
killHelper: 'Terminar el proceso {0} puede hacer que algunos programas funcionen incorrectamente. ¿Continuar?',
processName: 'Nombre del proceso',
},
tool: {

View file

@ -415,7 +415,7 @@ const message = {
ioDelay: 'I/Oレイテンシ',
uptime: 'それ以来',
runningTime: '稼働時間',
mem: 'システム',
mem: 'システムメモリ',
swapMem: 'パーティションを交換します',
runSmoothly: '低負荷',
@ -425,6 +425,9 @@ const message = {
core: '物理コア',
logicCore: '論理コア',
corePercent: 'コア使用率',
cpuTop: 'CPU使用率トップ5のプロセス情報',
memTop: 'メモリ使用率トップ5のプロセス情報',
loadAverage: '最後の1分で平均を積み込みます|最後の{n}分で平均を読み込みます',
load: '負荷',
mount: 'マウントポイント',
@ -2907,6 +2910,9 @@ const message = {
stopProcess: '終わり',
viewDetails: '詳細',
stopProcessWarn: 'このプロセスを終了したいですかPID:{0}',
kill: 'プロセス終了',
killNow: '今すぐ終了',
killHelper: 'プロセス {0} を終了すると一部のプログラムが正常に動作しなくなる可能性があります続行しますか',
processName: 'プロセス名',
},
tool: {

View file

@ -418,7 +418,7 @@ const message = {
ioDelay: 'I/O 지연 시간',
uptime: '작동 시간',
runningTime: '가동 시간',
mem: '시스템',
mem: '시스템 메모리',
swapMem: '스왑 파티션',
runSmoothly: '낮은 부하',
@ -428,6 +428,9 @@ const message = {
core: '물리적 코어',
logicCore: '논리 코어',
corePercent: '코어 사용률',
cpuTop: 'CPU 사용률 상위 5 프로세스 정보',
memTop: '메모리 사용률 상위 5 프로세스 정보',
loadAverage: '지난 1분의 평균 부하 | 지난 {n} 분의 평균 부하',
load: '부하',
mount: '마운트 지점',
@ -2856,6 +2859,10 @@ const message = {
stopProcess: '종료',
viewDetails: '세부 사항',
stopProcessWarn: ' 프로세스(PID:{0}) 종료하시겠습니까?',
kill: '프로세스 종료',
killNow: '즉시 종료',
killHelper:
'프로세스 {0}() 종료하면 일부 프로그램이 정상적으로 작동하지 않을 있습니다. 계속하시겠습니까?',
processName: '프로세스 이름',
},
tool: {

View file

@ -424,7 +424,7 @@ const message = {
ioDelay: 'I/O latency',
uptime: 'Up since',
runningTime: 'Uptime',
mem: 'System',
mem: 'Memori Sistem',
swapMem: 'Swap partition',
runSmoothly: 'Beban rendah',
@ -433,6 +433,9 @@ const message = {
runJam: 'Beban berat',
core: 'Teras Fizikal',
corePercent: 'Penggunaan Teras',
cpuTop: 'Maklumat Proses 5 Teratas Mengikut Penggunaan CPU',
memTop: 'Maklumat Proses 5 Teratas Mengikut Penggunaan Memori',
logicCore: 'Teras Logik',
loadAverage: 'Purata beban dalam 1 minit terakhir | Purata beban dalam {n} minit terakhir',
load: 'Beban',
@ -2973,6 +2976,10 @@ const message = {
stopProcess: 'End',
viewDetails: 'Details',
stopProcessWarn: 'Are you sure you want to end this process (PID:{0})?',
kill: 'Hentikan Proses',
killNow: 'Hentikan Sekarang',
killHelper:
'Menghentikan proses {0} mungkin menyebabkan beberapa program tidak berfungsi dengan normal. Teruskan?',
processName: 'Process name',
},
tool: {

View file

@ -422,7 +422,7 @@ const message = {
ioDelay: 'Latência de I/O',
uptime: 'Tempo de atividade',
runningTime: 'Tempo de execução',
mem: 'Memória',
mem: 'Memória do Sistema',
swapMem: 'Partição Swap',
runSmoothly: 'Baixo carregamento',
@ -432,6 +432,9 @@ const message = {
core: 'Núcleo físico',
logicCore: 'Núcleo lógico',
corePercent: 'Uso do Núcleo',
cpuTop: 'Top 5 Processos por Uso de CPU',
memTop: 'Top 5 Processos por Uso de Memória',
loadAverage: 'Média de carga nos últimos 1 minuto | Média de carga nos últimos {n} minutos',
load: 'Carga',
mount: 'Ponto de montagem',
@ -2978,6 +2981,9 @@ const message = {
stopProcess: 'Encerrar',
viewDetails: 'Detalhes',
stopProcessWarn: 'Tem certeza de que deseja encerrar este processo (PID:{0})?',
kill: 'Encerrar Processo',
killNow: 'Encerrar Agora',
killHelper: 'Encerrar o processo {0} pode fazer com que alguns programas funcionem incorretamente. Continuar?',
processName: 'Nome do Processo',
},
tool: {

View file

@ -419,7 +419,7 @@ const message = {
ioDelay: 'Задержка ввода/вывода',
uptime: 'Работает с',
runningTime: 'Время работы',
mem: 'Память',
mem: 'Системная Память',
swapMem: 'Раздел подкачки',
runSmoothly: 'Низкая нагрузка',
@ -429,6 +429,9 @@ const message = {
core: 'Физических ядер',
logicCore: 'Логических ядер',
corePercent: 'Использование Ядра',
cpuTop: 'Топ 5 Процессов по Использованию ЦПУ',
memTop: 'Топ 5 Процессов по Использованию Памяти',
loadAverage: 'Средняя нагрузка за последнюю минуту | Средняя нагрузка за последние {n} минут',
load: 'Нагрузка',
mount: 'Точка монтирования',
@ -2971,6 +2974,9 @@ const message = {
stopProcess: 'Завершить',
viewDetails: 'Подробности',
stopProcessWarn: 'Вы уверены, что хотите завершить этот процесс (PID:{0})?',
kill: 'Завершить Процесс',
killNow: 'Завершить Сейчас',
killHelper: 'Завершение процесса {0} может привести к некорректной работе некоторых программ. Продолжить?',
processName: 'Имя процесса',
},
tool: {

View file

@ -430,7 +430,7 @@ const message = {
ioDelay: 'G/Ç gecikmesi',
uptime: 'Çalışma süresi',
runningTime: 'Şu tarihten beri ık',
mem: 'Sistem',
mem: 'Sistem Belleği',
swapMem: 'Swap Bölümü',
runSmoothly: 'Düşük yük',
@ -440,6 +440,9 @@ const message = {
core: 'Fiziksel çekirdek',
logicCore: 'Mantıksal çekirdek',
corePercent: 'Çekirdek Kullanımı',
cpuTop: 'CPU Kullanımına Göre İlk 5 İşlem',
memTop: 'Bellek Kullanımına Göre İlk 5 İşlem',
loadAverage: 'Son 1 dakikadaki yük ortalaması | Son {n} dakikadaki yük ortalaması',
load: 'Yük',
mount: 'Bağlama noktası',
@ -3051,6 +3054,10 @@ const message = {
stopProcess: 'Sonlandır',
viewDetails: 'Detayları görüntüle',
stopProcessWarn: 'Bu işlemi (PID:{0}) sonlandırmak istediğinizden emin misiniz?',
kill: 'İşlemi Sonlandır',
killNow: 'Hemen Sonlandır',
killHelper:
'{0} işlemini sonlandırmak bazı programların düzgün çalışmamasına neden olabilir. Devam etmek istiyor musunuz?',
processName: 'İşlem adı',
},
tool: {

View file

@ -413,7 +413,7 @@ const message = {
ioDelay: '讀寫延遲',
uptime: '啟動時間',
runningTime: '執行時間',
mem: '系統',
mem: '系統記憶體',
swapMem: 'Swap 分區',
runSmoothly: '執行流暢',
@ -423,6 +423,9 @@ const message = {
core: '物理核心',
logicCore: '邏輯核心',
corePercent: '核心使用率',
cpuTop: 'CPU 佔用率 Top5 的行程資訊',
memTop: '記憶體佔用率 Top5 的行程資訊',
loadAverage: '最近 {0} 分鐘平均負載',
load: '負載',
mount: '掛載點',
@ -2796,6 +2799,9 @@ const message = {
stopProcess: '結束',
viewDetails: '查看詳情',
stopProcessWarn: '是否確定結束此行程 (PID:{0})',
kill: '結束行程',
killNow: '立即結束',
killHelper: '結束行程 {0} 可能導致部分程式無法正常運作是否繼續',
processName: '行程名稱',
},
tool: {

View file

@ -413,7 +413,7 @@ const message = {
ioDelay: '读写延迟',
uptime: '启动时间',
runningTime: '运行时间',
mem: '系统',
mem: '系统内存',
swapMem: 'Swap 分区',
runSmoothly: '运行流畅',
@ -423,6 +423,9 @@ const message = {
core: '物理核心',
logicCore: '逻辑核心',
corePercent: '核心使用率',
cpuTop: 'CPU 占用率 Top5 的进程信息',
memTop: '内存占用率 Top5 的进程信息',
loadAverage: '最近 {0} 分钟平均负载',
load: '负载',
mount: '挂载点',
@ -2790,6 +2793,9 @@ const message = {
stopProcess: '结束',
viewDetails: '查看详情',
stopProcessWarn: '是否确定结束此进程 (PID:{0})',
kill: '结束进程',
killNow: '立即结束',
killHelper: '结束进程 {0} 可能导致部分程序无法正常运行是否继续',
processName: '进程名称',
},
tool: {

View file

@ -1,5 +1,5 @@
<template>
<div :key="$route.fullPath">
<div :key="$route.fullPath" id="dashboard">
<RouterButton
:buttons="[
{
@ -425,6 +425,9 @@ const currentInfo = ref<Dashboard.CurrentInfo>({
netBytesSent: 0,
netBytesRecv: 0,
topCPUItems: [],
topMemItems: [],
shotTime: new Date(),
});
const currentChartInfo = reactive({

View file

@ -1,16 +1,37 @@
<template>
<el-row :gutter="10">
<el-col :xs="12" :sm="12" :md="6" :lg="6" :xl="6" align="center">
<el-popover placement="bottom" :width="200" trigger="hover" v-if="chartsOption['load']">
<el-tag class="tagClass">
{{ $t('home.loadAverage', [1]) }}: {{ formatNumber(currentInfo.load1) }}
</el-tag>
<el-tag class="tagClass">
{{ $t('home.loadAverage', [5]) }}: {{ formatNumber(currentInfo.load5) }}
</el-tag>
<el-tag class="tagClass">
{{ $t('home.loadAverage', [15]) }}: {{ formatNumber(currentInfo.load15) }}
</el-tag>
<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-descriptions :column="1" size="small">
<el-descriptions-item :label="$t('home.loadAverage', [1])">
{{ formatNumber(currentInfo.load1) }}
</el-descriptions-item>
<el-descriptions-item :label="$t('home.loadAverage', [5])">
{{ formatNumber(currentInfo.load5) }}
</el-descriptions-item>
<el-descriptions-item :label="$t('home.loadAverage', [15])">
{{ formatNumber(currentInfo.load15) }}
</el-descriptions-item>
</el-descriptions>
<el-button link size="small" type="primary" class="float-left mb-2" @click="showTop = !showTop">
{{ $t('home.cpuTop') }}
<el-icon v-if="!showTop"><ArrowRight /></el-icon>
<el-icon v-if="showTop"><ArrowDown /></el-icon>
</el-button>
<ComplexTable v-if="showTop" :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>
</el-table-column>
<el-table-column :width="80" :label="$t('commons.table.operate')">
<template #default="{ row }">
<el-button type="primary" link @click="onKill(row)">
{{ $t('process.stopProcess') }}
</el-button>
</template>
</el-table-column>
</ComplexTable>
<template #reference>
<v-charts
height="160px"
@ -23,42 +44,50 @@
</el-popover>
<span class="input-help">{{ loadStatus(currentInfo.loadUsagePercent) }}</span>
</el-col>
<el-col :xs="12" :sm="12" :md="6" :lg="6" :xl="6" align="center">
<el-popover placement="bottom" :width="loadWidth()" trigger="hover" v-if="chartsOption['cpu']">
<div>
<el-tooltip
effect="dark"
:content="baseInfo.cpuModelName"
v-if="baseInfo.cpuModelName.length > 40"
placement="top"
>
<el-tag class="cpuModeTag">
{{ baseInfo.cpuModelName.substring(0, 40) + '...' }}
</el-tag>
</el-tooltip>
<el-tag v-else>
{{ baseInfo.cpuModelName }}
</el-tag>
</div>
<el-tag class="cpuDetailTag">{{ $t('home.core') }} *{{ baseInfo.cpuCores }}</el-tag>
<el-tag class="cpuDetailTag">{{ $t('home.logicCore') }} *{{ baseInfo.cpuLogicalCores }}</el-tag>
<br />
<div v-for="(item, index) of currentInfo.cpuPercent" :key="index">
<el-tag v-if="cpuShowAll || (!cpuShowAll && index < 32)" class="tagCPUClass">
CPU-{{ index }}: {{ formatNumber(item) }}%
</el-tag>
<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-descriptions :title="baseInfo.cpuModelName" :column="2" size="small">
<el-descriptions-item :label="$t('home.core')">
{{ baseInfo.cpuCores }}
</el-descriptions-item>
<el-descriptions-item :label="$t('home.logicCore')">
{{ baseInfo.cpuLogicalCores }}
</el-descriptions-item>
</el-descriptions>
<el-button size="small" link type="primary" class="mb-2">
{{ $t('home.corePercent') }}
</el-button>
<el-space wrap :size="5" class="ml-1">
<template v-for="(item, index) of currentInfo.cpuPercent" :key="index">
<div class="cpu-detail" v-if="cpuShowAll || (!cpuShowAll && index < 8)">
CPU-{{ index }}: {{ formatNumber(item) }}%
</div>
</template>
</el-space>
<div v-if="currentInfo.cpuPercent.length > 8">
<el-button v-if="!cpuShowAll" @click="cpuShowAll = true" icon="More" link size="small" />
<el-button v-if="cpuShowAll" @click="cpuShowAll = false" icon="ArrowUp" link size="small" />
</div>
<div v-if="currentInfo.cpuPercent.length > 32" class="mt-1 float-right">
<el-button v-if="!cpuShowAll" @click="cpuShowAll = true" link type="primary" size="small">
{{ $t('commons.button.showAll') }}
<el-icon><DArrowRight /></el-icon>
</el-button>
<el-button v-if="cpuShowAll" @click="cpuShowAll = false" link type="primary" size="small">
{{ $t('commons.button.hideSome') }}
<el-icon><DArrowLeft /></el-icon>
</el-button>
</div>
<el-button link size="small" type="primary" class="mt-2 mb-2" @click="showTop = !showTop">
{{ $t('home.cpuTop') }}
<el-icon v-if="!showTop"><ArrowRight /></el-icon>
<el-icon v-if="showTop"><ArrowDown /></el-icon>
</el-button>
<ComplexTable v-if="showTop" :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>
</el-table-column>
<el-table-column :width="80" :label="$t('commons.table.operate')">
<template #default="{ row }">
<el-button type="primary" link @click="onKill(row)">
{{ $t('process.stopProcess') }}
</el-button>
</template>
</el-table-column>
</ComplexTable>
<template #reference>
<v-charts
height="160px"
@ -69,63 +98,84 @@
/>
</template>
</el-popover>
<span class="input-help">
( {{ formatNumber(currentInfo.cpuUsed) }} / {{ currentInfo.cpuTotal }} )
{{ $t('commons.units.core', currentInfo.cpuTotal) }}
</span>
<div class="text-center">
<span class="input-help">
( {{ formatNumber(currentInfo.cpuUsed) }} / {{ currentInfo.cpuTotal }} )
{{ $t('commons.units.core', currentInfo.cpuTotal) }}
</span>
</div>
</el-col>
<el-col :xs="12" :sm="12" :md="6" :lg="6" :xl="6" align="center">
<el-popover
placement="bottom"
:width="currentInfo.swapMemoryTotal ? 320 : 160"
trigger="hover"
v-if="chartsOption['memory']"
>
<el-row :gutter="5">
<el-col :span="currentInfo.swapMemoryTotal ? 12 : 24">
<el-row>
<el-tag style="font-weight: 500">{{ $t('home.mem') }}:</el-tag>
</el-row>
<el-tag class="tagClass">
{{ $t('home.total') }}: {{ computeSize(currentInfo.memoryTotal) }}
</el-tag>
<el-tag class="tagClass">
{{ $t('home.used') }}: {{ computeSize(currentInfo.memoryUsed) }}
</el-tag>
<el-tag class="tagClass">
{{ $t('home.free') }}: {{ computeSize(currentInfo.memoryFree) }}
</el-tag>
<el-tag class="tagClass">
{{ $t('home.shard') }}: {{ computeSize(currentInfo.memoryShard) }}
</el-tag>
<el-tag class="tagClass">
{{ $t('home.cache') }}: {{ computeSize(currentInfo.memoryCache) }}
</el-tag>
<el-tag class="tagClass">
{{ $t('home.available') }}: {{ computeSize(currentInfo.memoryAvailable) }}
</el-tag>
<el-tag class="tagClass">
{{ $t('home.percent') }}: {{ formatNumber(currentInfo.memoryUsedPercent) }}%
</el-tag>
</el-col>
<el-col :span="12" v-if="currentInfo.swapMemoryTotal" class="mt-20">
<el-row>
<el-tag style="font-weight: 500">{{ $t('home.swapMem') }}:</el-tag>
</el-row>
<el-tag class="tagClass">
{{ $t('home.total') }}: {{ computeSize(currentInfo.swapMemoryTotal) }}
</el-tag>
<el-tag class="tagClass">
{{ $t('home.used') }}: {{ computeSize(currentInfo.swapMemoryUsed) }}
</el-tag>
<el-tag class="tagClass">
{{ $t('home.free') }}: {{ computeSize(currentInfo.swapMemoryAvailable) }}
</el-tag>
<el-tag class="tagClass">
{{ $t('home.percent') }}: {{ formatNumber(currentInfo.swapMemoryUsedPercent) }}%
</el-tag>
</el-col>
</el-row>
<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-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) }}
</el-descriptions-item>
<el-descriptions-item :label="$t('home.used')">
{{ computeSize(currentInfo.memoryUsed) }}
</el-descriptions-item>
<el-descriptions-item :label="$t('home.free')">
{{ computeSize(currentInfo.memoryFree) }}
</el-descriptions-item>
<el-descriptions-item :label="$t('home.available')">
{{ computeSize(currentInfo.memoryAvailable) }}
</el-descriptions-item>
<el-descriptions-item :label="$t('home.shard')">
{{ computeSize(currentInfo.memoryShard) }}
</el-descriptions-item>
<el-descriptions-item :label="$t('home.cache')">
{{ computeSize(currentInfo.memoryCache) }}
</el-descriptions-item>
<el-descriptions-item :label="$t('home.percent')">
{{ formatNumber(currentInfo.memoryUsedPercent) }}%
</el-descriptions-item>
</el-descriptions>
<el-descriptions
v-if="currentInfo.swapMemoryTotal"
direction="vertical"
:title="$t('home.swapMem')"
:column="4"
size="small"
class="ml-1"
>
<el-descriptions-item :label-width="60" :label="$t('home.total')">
{{ computeSize(currentInfo.swapMemoryTotal) }}
</el-descriptions-item>
<el-descriptions-item :label-width="60" :label="$t('home.used')">
{{ computeSize(currentInfo.swapMemoryUsed) }}
</el-descriptions-item>
<el-descriptions-item :label-width="60" :label="$t('home.free')">
{{ computeSize(currentInfo.swapMemoryAvailable) }}
</el-descriptions-item>
<el-descriptions-item :label-width="60" :label="$t('home.percent')">
{{ formatNumber(currentInfo.swapMemoryUsedPercent) }}%
</el-descriptions-item>
</el-descriptions>
<el-button link size="small" type="primary" class="float-left mb-2" @click="showTop = !showTop">
{{ $t('home.memTop') }}
<el-icon v-if="!showTop"><ArrowRight /></el-icon>
<el-icon v-if="showTop"><ArrowDown /></el-icon>
</el-button>
<ComplexTable v-if="showTop" :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 }">
{{ computeSize(row.memory) }}
</template>
</el-table-column>
<el-table-column :min-width="80" :label="$t('monitor.percent')" prop="percent">
<template #default="{ row }">{{ row.percent.toFixed(2) }}%</template>
</el-table-column>
<el-table-column :width="80" :label="$t('commons.table.operate')">
<template #default="{ row }">
<el-button type="primary" link @click="onKill(row)">
{{ $t('process.stopProcess') }}
</el-button>
</template>
</el-table-column>
</ComplexTable>
<template #reference>
<v-charts
height="160px"
@ -141,48 +191,42 @@
</span>
</el-col>
<template v-for="(item, index) of currentInfo.diskData" :key="index">
<el-col :xs="12" :sm="12" :md="6" :lg="6" :xl="6" align="center" v-if="isShow('disk', index)">
<el-popover placement="bottom" :width="300" trigger="hover" v-if="chartsOption[`disk${index}`]">
<el-row :gutter="5">
<el-tag style="font-weight: 500">{{ $t('home.baseInfo') }}:</el-tag>
</el-row>
<el-row :gutter="5">
<el-tag class="nameTag">{{ $t('home.mount') }}: {{ item.path }}</el-tag>
</el-row>
<el-row :gutter="5">
<el-tag class="nameTag">{{ $t('commons.table.type') }}: {{ item.type }}</el-tag>
</el-row>
<el-row :gutter="5">
<el-tooltip :content="item.device" v-if="item.device.length > 30">
<el-tag class="nameTag">
{{ $t('home.fileSystem') }}: {{ item.device.substring(0, 30) + '...' }}
</el-tag>
</el-tooltip>
<el-tag v-else class="nameTag">{{ $t('home.fileSystem') }}: {{ item.device }}</el-tag>
</el-row>
<el-row :gutter="5">
<el-col :span="12">
<div><el-tag class="nameTag" style="font-weight: 500">Inode:</el-tag></div>
<el-tag class="tagClass">{{ $t('home.total') }}: {{ item.inodesTotal }}</el-tag>
<el-tag class="tagClass">{{ $t('home.used') }}: {{ item.inodesUsed }}</el-tag>
<el-tag class="tagClass">{{ $t('home.free') }}: {{ item.inodesFree }}</el-tag>
<el-tag class="tagClass">
{{ $t('home.percent') }}: {{ formatNumber(item.inodesUsedPercent) }}%
</el-tag>
</el-col>
<el-col :xs="6" :sm="6" :md="3" :lg="3" :xl="3" align="center" v-if="isShow('disk', index)">
<el-popover :hide-after="20" :teleported="false" :width="450" v-if="chartsOption[`disk${index}`]">
<el-descriptions :column="1" size="small">
<el-descriptions-item :label="$t('home.mount')">
{{ item.path }}
</el-descriptions-item>
<el-descriptions-item :label="$t('commons.table.type')">
{{ item.type }}
</el-descriptions-item>
<el-descriptions-item :label="$t('home.fileSystem')">
{{ item.device }}
</el-descriptions-item>
</el-descriptions>
<el-descriptions title="Inode" direction="vertical" :column="4" size="small">
<el-descriptions-item :label="$t('home.total')">{{ item.inodesTotal }}</el-descriptions-item>
<el-descriptions-item :label="$t('home.used')">{{ item.inodesUsed }}</el-descriptions-item>
<el-descriptions-item :label="$t('home.free')">{{ item.inodesFree }}</el-descriptions-item>
<el-descriptions-item :label="$t('home.percent')">
{{ formatNumber(item.inodesUsedPercent) }}%
</el-descriptions-item>
</el-descriptions>
<el-col :span="12">
<div>
<el-tag style="margin-top: 3px; font-weight: 500">{{ $t('monitor.disk') }}:</el-tag>
</div>
<el-tag class="tagClass">{{ $t('home.total') }}: {{ computeSize(item.total) }}</el-tag>
<el-tag class="tagClass">{{ $t('home.used') }}: {{ computeSize(item.used) }}</el-tag>
<el-tag class="tagClass">{{ $t('home.free') }}: {{ computeSize(item.free) }}</el-tag>
<el-tag class="tagClass">
{{ $t('home.percent') }}: {{ formatNumber(item.usedPercent) }}%
</el-tag>
</el-col>
</el-row>
<el-descriptions :title="$t('monitor.disk')" direction="vertical" :column="4" size="small">
<el-descriptions-item :label="$t('home.total')">
{{ computeSize(item.total) }}
</el-descriptions-item>
<el-descriptions-item :label="$t('home.used')">
{{ computeSize(item.used) }}
</el-descriptions-item>
<el-descriptions-item :label="$t('home.free')">
{{ computeSize(item.free) }}
</el-descriptions-item>
<el-descriptions-item :label="$t('home.percent')">
{{ formatNumber(item.usedPercent) }}%
</el-descriptions-item>
</el-descriptions>
<template #reference>
<v-charts
@click="routerToFileWithPath(item.path)"
@ -198,19 +242,28 @@
</el-col>
</template>
<template v-for="(item, index) of currentInfo.gpuData" :key="index">
<el-col :xs="12" :sm="12" :md="6" :lg="6" :xl="6" align="center" v-if="isShow('gpu', index)">
<el-popover placement="bottom" :width="250" trigger="hover" v-if="chartsOption[`gpu${index}`]">
<el-row :gutter="5">
<el-tag class="nameTag" style="font-weight: 500">{{ $t('home.baseInfo') }}:</el-tag>
</el-row>
<el-tag class="tagClass">{{ $t('monitor.gpuUtil') }}: {{ item.gpuUtil }}</el-tag>
<el-tag class="tagClass">
{{ $t('monitor.temperature') }}: {{ item.temperature.replaceAll('C', '°C') }}
</el-tag>
<el-tag class="tagClass">{{ $t('monitor.performanceState') }}: {{ item.performanceState }}</el-tag>
<el-tag class="tagClass">{{ $t('monitor.powerUsage') }}: {{ item.powerUsage }}</el-tag>
<el-tag class="tagClass">{{ $t('monitor.memoryUsage') }}: {{ item.memoryUsage }}</el-tag>
<el-tag class="tagClass">{{ $t('monitor.fanSpeed') }}: {{ item.fanSpeed }}</el-tag>
<el-col :xs="6" :sm="6" :md="3" :lg="3" :xl="3" align="center" v-if="isShow('gpu', index)">
<el-popover :hide-after="20" :teleported="false" :width="450" v-if="chartsOption[`gpu${index}`]">
<el-descriptions :title="item.productName" direction="vertical" :column="3" size="small">
<el-descriptions-item :label="$t('monitor.gpuUtil')">
{{ item.gpuUtil }}
</el-descriptions-item>
<el-descriptions-item :label="$t('monitor.temperature')">
{{ item.temperature.replaceAll('C', '°C') }}
</el-descriptions-item>
<el-descriptions-item :label="$t('monitor.performanceState')">
{{ item.performanceState }}
</el-descriptions-item>
<el-descriptions-item :label="$t('monitor.powerUsage')">
{{ item.powerUsage }}
</el-descriptions-item>
<el-descriptions-item :label="$t('monitor.memoryUsage')">
{{ item.memoryUsage }}
</el-descriptions-item>
<el-descriptions-item :label="$t('monitor.fanSpeed')">
{{ item.fanSpeed }}
</el-descriptions-item>
</el-descriptions>
<template #reference>
<v-charts
@click="goGPU()"
@ -229,17 +282,22 @@
</el-col>
</template>
<template v-for="(item, index) of currentInfo.xpuData" :key="index">
<el-col :xs="12" :sm="12" :md="6" :lg="6" :xl="6" align="center" v-if="isShow('xpu', index)">
<el-popover placement="bottom" :width="250" trigger="hover" v-if="chartsOption[`xpu${index}`]">
<el-row :gutter="5">
<el-tag class="nameTag">{{ $t('home.baseInfo') }}:</el-tag>
</el-row>
<el-tag class="tagClass">{{ $t('monitor.gpuUtil') }}: {{ item.memoryUtil }}</el-tag>
<el-tag class="tagClass">{{ $t('monitor.temperature') }}: {{ item.temperature }}</el-tag>
<el-tag class="tagClass">{{ $t('monitor.powerUsage') }}: {{ item.power }}</el-tag>
<el-tag class="tagClass">
{{ $t('monitor.memoryUsage') }}: {{ item.memoryUsed }}/{{ item.memory }}
</el-tag>
<el-col :xs="6" :sm="6" :md="3" :lg="3" :xl="3" align="center" v-if="isShow('xpu', index)">
<el-popover :hide-after="20" :teleported="false" :width="400" v-if="chartsOption[`xpu${index}`]">
<el-descriptions :title="item.deviceName" direction="vertical" :column="4" size="small">
<el-descriptions-item :label="$t('monitor.gpuUtil')">
{{ item.memoryUtil }}
</el-descriptions-item>
<el-descriptions-item :label="$t('monitor.temperature')">
{{ item.temperature }}
</el-descriptions-item>
<el-descriptions-item :label="$t('monitor.powerUsage')">
{{ item.power }}
</el-descriptions-item>
<el-descriptions-item :label="$t('monitor.memoryUsage')">
{{ item.memoryUsed }}/{{ item.memory }}
</el-descriptions-item>
</el-descriptions>
<template #reference>
<v-charts
@click="goGPU()"
@ -257,7 +315,7 @@
<span class="input-help" v-else>{{ item.deviceName }}</span>
</el-col>
</template>
<el-col :xs="12" :sm="12" :md="6" :lg="6" :xl="6" align="center" v-if="totalCount > 5">
<el-col :xs="6" :sm="6" :md="6" :lg="6" :xl="6" align="center" v-if="totalCount > 5">
<el-button v-if="!showMore" link type="primary" @click="changeShowMore(true)" class="buttonClass">
{{ $t('tabs.more') }}
<el-icon><Bottom /></el-icon>
@ -267,7 +325,8 @@
<el-icon><Top /></el-icon>
</el-button>
</el-col>
</el-row>
<ConfirmDialog ref="confirmConfRef" @confirm="submitKill" />
</div>
</template>
<script setup lang="ts">
@ -276,6 +335,8 @@ import { computeSize } from '@/utils/util';
import i18n from '@/lang';
import { nextTick, ref } from 'vue';
import { routerToFileWithPath, routerToName } from '@/utils/router';
import { stopProcess } from '@/api/modules/process';
import { MsgSuccess } from '@/utils/message';
const showMore = ref(false);
const totalCount = ref();
@ -334,12 +395,18 @@ const currentInfo = ref<Dashboard.CurrentInfo>({
gpuData: [],
xpuData: [],
topCPUItems: [],
topMemItems: [],
netBytesSent: 0,
netBytesRecv: 0,
shotTime: new Date(),
});
const cpuShowAll = ref();
const showTop = ref();
const killProcessID = ref();
const confirmConfRef = ref();
const chartsOption = ref({
cpu: { title: 'CPU', data: 0 },
@ -411,6 +478,21 @@ const changeShowMore = (show: boolean) => {
localStorage.setItem('dashboard_show', show ? 'more' : 'hide');
};
const onKill = async (row: any) => {
let params = {
header: i18n.global.t('process.kill'),
operationInfo: i18n.global.t('process.killHelper'),
submitInputInfo: i18n.global.t('process.killNow'),
};
killProcessID.value = row.pid;
confirmConfRef.value!.acceptParams(params);
};
const submitKill = async () => {
await stopProcess({ PID: killProcessID.value }).then(() => {
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
});
};
function loadStatus(val: number) {
if (val < 30) {
return i18n.global.t('home.runSmoothly');
@ -428,14 +510,6 @@ const goGPU = () => {
routerToName('GPU');
};
const loadWidth = () => {
if (!cpuShowAll.value || currentInfo.value.cpuPercent.length < 32) {
return 310;
}
let line = Math.floor(currentInfo.value.cpuPercent.length / 16);
return line * 141 + 28;
};
function formatNumber(val: number) {
return Number(val.toFixed(2));
}
@ -446,44 +520,52 @@ defineExpose({
</script>
<style scoped lang="scss">
.cpuModeTag {
justify-content: flex-start !important;
text-align: left !important;
width: 280px;
}
.cpuDetailTag {
justify-content: flex-start !important;
text-align: left !important;
width: 140px;
margin-top: 3px;
margin-left: 1px;
}
.tagClass {
justify-content: flex-start !important;
text-align: left !important;
float: left;
margin-top: 3px;
width: 100%;
}
.tagCPUClass {
justify-content: flex-start !important;
text-align: left !important;
float: left;
margin-top: 3px;
margin-left: 1px;
width: 140px;
}
.buttonClass {
margin-top: 28%;
font-size: 14px;
}
.nameTag {
margin-top: 3px;
height: auto;
display: inline-block;
white-space: normal;
line-height: 1.8;
.cpu-detail {
font-size: 12px;
width: 95px;
}
:deep(.el-descriptions__label) {
width: 80px;
background-color: transparent !important;
}
.custom-row {
display: grid;
grid-template-columns: repeat(12, 1fr);
gap: 10px;
width: 100%;
}
.custom-row .el-col {
width: 100% !important;
max-width: 100% !important;
flex: none !important;
float: none !important;
display: block !important;
}
.custom-row .el-col.el-col-xs-6 {
grid-column: span 6;
}
@media (min-width: 768px) {
.custom-row .el-col.el-col-sm-6 {
grid-column: span 6;
}
}
@media (min-width: 992px) {
.custom-row .el-col.el-col-md-3 {
grid-column: span 3;
}
}
@media (min-width: 1200px) {
.custom-row .el-col.el-col-lg-3 {
grid-column: span 3;
}
}
@media (min-width: 1920px) {
.custom-row .el-col.el-col-xl-3 {
grid-column: span 3;
}
}
</style>

View file

@ -609,7 +609,7 @@ function withCPUProcess(datas: any) {
res += loadSeries(item, item.data.value ? item.data.value : item.data, item.data.unit || '');
}
if (!tops) {
return '';
return res;
}
res += `
<div style="margin-top: 10px; border-bottom: 1px dashed black;"></div>