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"` GPUData []GPUInfo `json:"gpuData"`
XPUData []XPUInfo `json:"xpuData"` XPUData []XPUInfo `json:"xpuData"`
TopCPUItems []Process `json:"topCPUItems"`
TopMemItems []Process `json:"topMemItems"`
ShotTime time.Time `json:"shotTime"` ShotTime time.Time `json:"shotTime"`
} }

View file

@ -120,7 +120,7 @@ func (u *DashboardService) LoadCurrentInfoForNode() *dto.NodeCurrent {
memoryInfo, _ := mem.VirtualMemory() memoryInfo, _ := mem.VirtualMemory()
currentInfo.MemoryTotal = memoryInfo.Total currentInfo.MemoryTotal = memoryInfo.Total
currentInfo.MemoryAvailable = memoryInfo.Available currentInfo.MemoryAvailable = memoryInfo.Available
currentInfo.MemoryUsed = memoryInfo.Used + memoryInfo.Shared currentInfo.MemoryUsed = memoryInfo.Used
currentInfo.MemoryUsedPercent = memoryInfo.UsedPercent currentInfo.MemoryUsedPercent = memoryInfo.UsedPercent
swapInfo, _ := mem.SwapMemory() swapInfo, _ := mem.SwapMemory()
@ -194,7 +194,7 @@ func (u *DashboardService) LoadCurrentInfo(ioOption string, netOption string) *d
memoryInfo, _ := mem.VirtualMemory() memoryInfo, _ := mem.VirtualMemory()
currentInfo.MemoryTotal = memoryInfo.Total currentInfo.MemoryTotal = memoryInfo.Total
currentInfo.MemoryUsed = memoryInfo.Used + memoryInfo.Shared currentInfo.MemoryUsed = memoryInfo.Used
currentInfo.MemoryFree = memoryInfo.Free currentInfo.MemoryFree = memoryInfo.Free
currentInfo.MemoryCache = memoryInfo.Cached + memoryInfo.Buffers currentInfo.MemoryCache = memoryInfo.Cached + memoryInfo.Buffers
currentInfo.MemoryShard = memoryInfo.Shared currentInfo.MemoryShard = memoryInfo.Shared
@ -211,6 +211,9 @@ func (u *DashboardService) LoadCurrentInfo(ioOption string, netOption string) *d
currentInfo.GPUData = loadGPUInfo() currentInfo.GPUData = loadGPUInfo()
currentInfo.XPUData = loadXpuInfo() currentInfo.XPUData = loadXpuInfo()
currentInfo.TopCPUItems = loadTopCPU()
currentInfo.TopMemItems = loadTopMem()
if ioOption == "all" { if ioOption == "all" {
diskInfo, _ := disk.IOCounters() diskInfo, _ := disk.IOCounters()
for _, state := range diskInfo { for _, state := range diskInfo {

View file

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

View file

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

View file

@ -426,7 +426,7 @@ const message = {
ioDelay: 'I/O latency', ioDelay: 'I/O latency',
uptime: 'Uptime', uptime: 'Uptime',
runningTime: 'Up since', runningTime: 'Up since',
mem: 'System', mem: 'System Memory',
swapMem: 'Swap Partition', swapMem: 'Swap Partition',
runSmoothly: 'Low load', runSmoothly: 'Low load',
@ -436,6 +436,9 @@ const message = {
core: 'Physical core', core: 'Physical core',
logicCore: 'Logical 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', loadAverage: 'Load average in the last 1 minute | Load average in the last {n} minutes',
load: 'Load', load: 'Load',
mount: 'Mount point', mount: 'Mount point',
@ -3009,6 +3012,9 @@ const message = {
stopProcess: 'End', stopProcess: 'End',
viewDetails: 'View details', viewDetails: 'View details',
stopProcessWarn: 'Are you sure you want to end this process (PID:{0})?', 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', processName: 'Process name',
}, },
tool: { tool: {

View file

@ -434,7 +434,7 @@ const message = {
ioDelay: 'Latencia de E/S', ioDelay: 'Latencia de E/S',
uptime: 'Tiempo en funcionamiento', uptime: 'Tiempo en funcionamiento',
runningTime: 'Desde', runningTime: 'Desde',
mem: 'Sistema', mem: 'Memoria del Sistema',
swapMem: 'Partición swap', swapMem: 'Partición swap',
runSmoothly: 'Carga baja', runSmoothly: 'Carga baja',
runNormal: 'Carga moderada', runNormal: 'Carga moderada',
@ -442,6 +442,9 @@ const message = {
runJam: 'Carga pesada', runJam: 'Carga pesada',
core: 'Núcleo físico', core: 'Núcleo físico',
logicCore: 'Núcleo lógico', 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', loadAverage: 'Promedio de carga en el último minuto | Promedio de carga en los últimos {n} minutos',
load: 'Carga', load: 'Carga',
mount: 'Punto de montaje', mount: 'Punto de montaje',
@ -2982,6 +2985,9 @@ const message = {
stopProcess: 'Finalizar', stopProcess: 'Finalizar',
viewDetails: 'Ver detalles', viewDetails: 'Ver detalles',
stopProcessWarn: '¿Seguro que deseas finalizar este proceso (PID:{0})?', 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', processName: 'Nombre del proceso',
}, },
tool: { tool: {

View file

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

View file

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

View file

@ -424,7 +424,7 @@ const message = {
ioDelay: 'I/O latency', ioDelay: 'I/O latency',
uptime: 'Up since', uptime: 'Up since',
runningTime: 'Uptime', runningTime: 'Uptime',
mem: 'System', mem: 'Memori Sistem',
swapMem: 'Swap partition', swapMem: 'Swap partition',
runSmoothly: 'Beban rendah', runSmoothly: 'Beban rendah',
@ -433,6 +433,9 @@ const message = {
runJam: 'Beban berat', runJam: 'Beban berat',
core: 'Teras Fizikal', 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', logicCore: 'Teras Logik',
loadAverage: 'Purata beban dalam 1 minit terakhir | Purata beban dalam {n} minit terakhir', loadAverage: 'Purata beban dalam 1 minit terakhir | Purata beban dalam {n} minit terakhir',
load: 'Beban', load: 'Beban',
@ -2973,6 +2976,10 @@ const message = {
stopProcess: 'End', stopProcess: 'End',
viewDetails: 'Details', viewDetails: 'Details',
stopProcessWarn: 'Are you sure you want to end this process (PID:{0})?', 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', processName: 'Process name',
}, },
tool: { tool: {

View file

@ -422,7 +422,7 @@ const message = {
ioDelay: 'Latência de I/O', ioDelay: 'Latência de I/O',
uptime: 'Tempo de atividade', uptime: 'Tempo de atividade',
runningTime: 'Tempo de execução', runningTime: 'Tempo de execução',
mem: 'Memória', mem: 'Memória do Sistema',
swapMem: 'Partição Swap', swapMem: 'Partição Swap',
runSmoothly: 'Baixo carregamento', runSmoothly: 'Baixo carregamento',
@ -432,6 +432,9 @@ const message = {
core: 'Núcleo físico', core: 'Núcleo físico',
logicCore: 'Núcleo lógico', 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', loadAverage: 'Média de carga nos últimos 1 minuto | Média de carga nos últimos {n} minutos',
load: 'Carga', load: 'Carga',
mount: 'Ponto de montagem', mount: 'Ponto de montagem',
@ -2978,6 +2981,9 @@ const message = {
stopProcess: 'Encerrar', stopProcess: 'Encerrar',
viewDetails: 'Detalhes', viewDetails: 'Detalhes',
stopProcessWarn: 'Tem certeza de que deseja encerrar este processo (PID:{0})?', 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', processName: 'Nome do Processo',
}, },
tool: { tool: {

View file

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

View file

@ -430,7 +430,7 @@ const message = {
ioDelay: 'G/Ç gecikmesi', ioDelay: 'G/Ç gecikmesi',
uptime: 'Çalışma süresi', uptime: 'Çalışma süresi',
runningTime: 'Şu tarihten beri ık', runningTime: 'Şu tarihten beri ık',
mem: 'Sistem', mem: 'Sistem Belleği',
swapMem: 'Swap Bölümü', swapMem: 'Swap Bölümü',
runSmoothly: 'Düşük yük', runSmoothly: 'Düşük yük',
@ -440,6 +440,9 @@ const message = {
core: 'Fiziksel çekirdek', core: 'Fiziksel çekirdek',
logicCore: 'Mantıksal ç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ı', loadAverage: 'Son 1 dakikadaki yük ortalaması | Son {n} dakikadaki yük ortalaması',
load: 'Yük', load: 'Yük',
mount: 'Bağlama noktası', mount: 'Bağlama noktası',
@ -3051,6 +3054,10 @@ const message = {
stopProcess: 'Sonlandır', stopProcess: 'Sonlandır',
viewDetails: 'Detayları görüntüle', viewDetails: 'Detayları görüntüle',
stopProcessWarn: 'Bu işlemi (PID:{0}) sonlandırmak istediğinizden emin misiniz?', 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ı', processName: 'İşlem adı',
}, },
tool: { tool: {

View file

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

View file

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

View file

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

View file

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