fix: 概览界面实时数据展示

This commit is contained in:
ssongliu 2022-11-24 23:56:48 +08:00 committed by ssongliu
parent e748f493da
commit 8ceb75b195
25 changed files with 1217 additions and 346 deletions

View File

@ -0,0 +1,43 @@
package v1
import (
"errors"
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/gin-gonic/gin"
)
func (b *BaseApi) LoadDashboardBaseInfo(c *gin.Context) {
ioOption, ok := c.Params.Get("ioOption")
if !ok {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, errors.New("error ioOption in path"))
return
}
netOption, ok := c.Params.Get("netOption")
if !ok {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, errors.New("error netOption in path"))
return
}
data, err := dashboardService.LoadBaseInfo(ioOption, netOption)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, data)
}
func (b *BaseApi) LoadDashboardCurrentInfo(c *gin.Context) {
ioOption, ok := c.Params.Get("ioOption")
if !ok {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, errors.New("error ioOption in path"))
return
}
netOption, ok := c.Params.Get("netOption")
if !ok {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, errors.New("error netOption in path"))
return
}
data := dashboardService.LoadCurrentInfo(ioOption, netOption)
helper.SuccessWithData(c, data)
}

View File

@ -0,0 +1,16 @@
package v1
import (
"fmt"
"testing"
"github.com/shirou/gopsutil/net"
)
func TestCopu(t *testing.T) {
fmt.Println(net.IOCounters(false))
fmt.Println(net.IOCounters(true))
}
// [{"name":"all","bytesSent":15498384367,"bytesRecv":18415197440,"packetsSent":11608058,"packetsRecv":12976591,"errin":0,"errout":21,"dropin":0,"dropout":1181,"fifoin":0,"fifoout":0}] <nil>
// [{"name":"lo0","bytesSent":12947010452,"bytesRecv":12947010452,"packetsSent":6519135,"packetsRecv":6519135,"errin":0,"errout":0,"dropin":0,"dropout":0,"fifoin":0,"fifoout":0} {"name":"gif0","bytesSent":0,"bytesRecv":0,"packetsSent":0,"packetsRecv":0,"errin":0,"errout":0,"dropin":0,"dropout":0,"fifoin":0,"fifoout":0} {"name":"stf0","bytesSent":0,"bytesRecv":0,"packetsSent":0,"packetsRecv":0,"errin":0,"errout":0,"dropin":0,"dropout":0,"fifoin":0,"fifoout":0} {"name":"ap1","bytesSent":0,"bytesRecv":0,"packetsSent":0,"packetsRecv":0,"errin":0,"errout":0,"dropin":0,"dropout":0,"fifoin":0,"fifoout":0} {"name":"en0","bytesSent":2531548007,"bytesRecv":5443547837,"packetsSent":5019082,"packetsRecv":6374381,"errin":0,"errout":0,"dropin":0,"dropout":1065,"fifoin":0,"fifoout":0} {"name":"en1","bytesSent":0,"bytesRecv":0,"packetsSent":0,"packetsRecv":0,"errin":0,"errout":0,"dropin":0,"dropout":0,"fifoin":0,"fifoout":0} {"name":"en2","bytesSent":0,"bytesRecv":0,"packetsSent":0,"packetsRecv":0,"errin":0,"errout":0,"dropin":0,"dropout":0,"fifoin":0,"fifoout":0} {"name":"bridge0","bytesSent":0,"bytesRecv":0,"packetsSent":0,"packetsRecv":0,"errin":0,"errout":0,"dropin":0,"dropout":0,"fifoin":0,"fifoout":0} {"name":"awdl0","bytesSent":103717,"bytesRecv":157244,"packetsSent":412,"packetsRecv":460,"errin":0,"errout":0,"dropin":0,"dropout":0,"fifoin":0,"fifoout":0} {"name":"llw0","bytesSent":0,"bytesRecv":0,"packetsSent":0,"packetsRecv":0,"errin":0,"errout":0,"dropin":0,"dropout":0,"fifoin":0,"fifoout":0} {"name":"utun0","bytesSent":21810,"bytesRecv":0,"packetsSent":121,"packetsRecv":0,"errin":0,"errout":0,"dropin":0,"dropout":0,"fifoin":0,"fifoout":0} {"name":"utun1","bytesSent":21810,"bytesRecv":0,"packetsSent":121,"packetsRecv":0,"errin":0,"errout":0,"dropin":0,"dropout":0,"fifoin":0,"fifoout":0} {"name":"utun2","bytesSent":21810,"bytesRecv":0,"packetsSent":121,"packetsRecv":0,"errin":0,"errout":0,"dropin":0,"dropout":0,"fifoin":0,"fifoout":0} {"name":"en5","bytesSent":6093969,"bytesRecv":6264468,"packetsSent":47985,"packetsRecv":48051,"errin":0,"errout":21,"dropin":0,"dropout":116,"fifoin":0,"fifoout":0} {"name":"utun3","bytesSent":13576933,"bytesRecv":18231580,"packetsSent":21129,"packetsRecv":34612,"errin":0,"errout":0,"dropin":0,"dropout":0,"fifoin":0,"fifoout":0}] <nil>

View File

@ -9,7 +9,8 @@ type ApiGroup struct {
var ApiGroupApp = new(ApiGroup)
var (
authService = service.ServiceGroupApp.AuthService
authService = service.ServiceGroupApp.AuthService
dashboardService = service.ServiceGroupApp.DashboardService
appService = service.ServiceGroupApp.AppService
appInstallService = service.ServiceGroupApp.AppInstallService

View File

@ -9,6 +9,7 @@ import (
"github.com/1Panel-dev/1Panel/backend/constant"
"github.com/1Panel-dev/1Panel/backend/global"
"github.com/gin-gonic/gin"
"github.com/shirou/gopsutil/disk"
"github.com/shirou/gopsutil/net"
)
@ -91,3 +92,13 @@ func (b *BaseApi) GetNetworkOptions(c *gin.Context) {
}
helper.SuccessWithData(c, options)
}
func (b *BaseApi) GetIOOptions(c *gin.Context) {
diskStat, _ := disk.IOCounters()
var options []string
options = append(options, "all")
for _, net := range diskStat {
options = append(options, net.Name)
}
helper.SuccessWithData(c, options)
}

View File

@ -1,26 +1,71 @@
package dto
import "time"
type DashboardBase struct {
HaloEnabled bool `json:"haloEnabled"`
DateeaseEnabled bool `json:"dateeaseEnabled"`
JumpServerEnabled bool `json:"jumpserverEnabled"`
MeterSphereEnabled bool `json:"metersphereEnabled"`
HaloID uint `json:"haloID"`
DateeaseID uint `json:"dateeaseID"`
JumpServerID uint `json:"jumpserverID"`
MeterSphereID uint `json:"metersphereID"`
KubeoperatorID uint `json:"kubeoperatorID"`
KubepiID uint `json:"kubepiID"`
WebsiteNumber int `json:"websiteNumber"`
DatabaseNumber int `json:"databaseNumber"`
CronjobNumber int `json:"cronjobNumber"`
AppInstalldNumber int `json:"appInstalldNumber"`
HostName string `json:"hostname"`
Os string `json:"os"`
Hostname string `json:"hostname"`
OS string `json:"os"`
Platform string `json:"platform"`
PlatformFamily string `json:"platformFamily"`
PlatformVersion string `json:"platformVersion"`
KernelArch string `json:"kernelArch"`
KernelVersion string `json:"kernelVersion"`
VirtualizationSystem string `json:"virtualizationSystem"`
CPUCores int `json:"cpuCores"`
CPULogicalCores int `json:"cpuLogicalCores"`
CPUModelName string `json:"cpuModelName"`
CPUPercent float64 `json:"cpuPercent"`
CPUCores int `json:"cpuCores"`
CPULogicalCores int `json:"cpuLogicalCores"`
CPUModelName string `json:"cpuModelName"`
CurrentInfo DashboardCurrent `json:"currentInfo"`
}
type DashboardCurrent struct {
Procs uint64 `json:"procs"`
Load1 float64 `json:"load1"`
Load5 float64 `json:"load5"`
Load15 float64 `json:"load15"`
LoadUsagePercent float64 `json:"loadUsagePercent"`
CPUPercent []float64 `json:"cpuPercent"`
CPUUsedPercent float64 `json:"cpuUsedPercent"`
CPUUsed float64 `json:"cpuUsed"`
CPUTotal int `json:"cpuTotal"`
MemoryTotal uint64 `json:"memoryTotal"`
MemoryAvailable uint64 `json:"memoryAvailable"`
MemoryUsed uint64 `json:"memoryUsed"`
MemoryUsedPercent float64 `json:"MemoryUsedPercent"`
IOReadBytes uint64 `json:"ioReadBytes"`
IOWriteBytes uint64 `json:"ioWriteBytes"`
IOCount uint64 `json:"ioCount"`
IOTime uint64 `json:"ioTime"`
Total uint64 `json:"total"`
Free uint64 `json:"free"`
Used uint64 `json:"used"`
UsedPercent float64 `json:"usedPercent"`
InodesTotal uint64 `json:"inodesTotal"`
InodesUsed uint64 `json:"inodesUsed"`
InodesFree uint64 `json:"inodesFree"`
InodesUsedPercent float64 `json:"inodesUsedPercent"`
NetBytesSent uint64 `json:"netBytesSent"`
NetBytesRecv uint64 `json:"netBytesRecv"`
ShotTime time.Time `json:"shotTime"`
}

View File

@ -1,54 +1,80 @@
package service
import (
"encoding/json"
"time"
"github.com/1Panel-dev/1Panel/backend/app/dto"
"github.com/jinzhu/copier"
"github.com/shirou/gopsutil/cpu"
"github.com/shirou/gopsutil/disk"
"github.com/shirou/gopsutil/host"
"github.com/shirou/gopsutil/load"
"github.com/shirou/gopsutil/mem"
"github.com/shirou/gopsutil/net"
)
type DashboardService struct{}
type IDashboardService interface {
LoadBaseInfo() (*dto.DashboardBase, error)
LoadBaseInfo(ioOption string, netOption string) (*dto.DashboardBase, error)
LoadCurrentInfo(ioOption string, netOption string) *dto.DashboardCurrent
}
func NewIDashboardService() IDashboardService {
return &DashboardService{}
}
func (u *DashboardService) LoadBaseInfo() (*dto.DashboardBase, error) {
func (u *DashboardService) LoadBaseInfo(ioOption string, netOption string) (*dto.DashboardBase, error) {
var baseInfo dto.DashboardBase
hostInfo, err := host.Info()
if err != nil {
return nil, err
}
if err := copier.Copy(baseInfo, hostInfo); err != nil {
return nil, err
}
appInstall, err := appInstallRepo.GetBy()
baseInfo.Hostname = hostInfo.Hostname
baseInfo.OS = hostInfo.OS
baseInfo.Platform = hostInfo.Platform
baseInfo.PlatformFamily = hostInfo.PlatformFamily
baseInfo.PlatformVersion = hostInfo.PlatformVersion
baseInfo.KernelArch = hostInfo.KernelArch
baseInfo.KernelVersion = hostInfo.KernelVersion
ss, _ := json.Marshal(hostInfo)
baseInfo.VirtualizationSystem = string(ss)
apps, err := appRepo.GetBy()
if err != nil {
return nil, err
}
for _, app := range appInstall {
switch app.App.Key {
for _, app := range apps {
switch app.Key {
case "dateease":
baseInfo.DateeaseEnabled = true
baseInfo.DateeaseID = app.ID
case "halo":
baseInfo.HaloEnabled = true
baseInfo.HaloID = app.ID
case "metersphere":
baseInfo.MeterSphereEnabled = true
baseInfo.MeterSphereID = app.ID
case "jumpserver":
baseInfo.JumpServerEnabled = true
baseInfo.JumpServerID = app.ID
case "kubeoperator":
baseInfo.KubeoperatorID = app.ID
case "kubepi":
baseInfo.KubepiID = app.ID
}
}
appInstall, err := appInstallRepo.GetBy()
if err != nil {
return nil, err
}
baseInfo.AppInstalldNumber = len(appInstall)
dbs, err := mysqlRepo.List()
if err != nil {
return nil, err
}
baseInfo.DatabaseNumber = len(dbs)
website, err := websiteRepo.GetBy()
if err != nil {
return nil, err
}
baseInfo.WebsiteNumber = len(website)
cornjobs, err := cronjobRepo.List()
if err != nil {
return nil, err
@ -62,10 +88,85 @@ func (u *DashboardService) LoadBaseInfo() (*dto.DashboardBase, error) {
baseInfo.CPUModelName = cpuInfo[0].ModelName
baseInfo.CPUCores, _ = cpu.Counts(false)
baseInfo.CPULogicalCores, _ = cpu.Counts(true)
totalPercent, _ := cpu.Percent(1*time.Second, false)
if len(totalPercent) == 1 {
baseInfo.CPUPercent = totalPercent[0]
}
baseInfo.CurrentInfo = *u.LoadCurrentInfo(ioOption, netOption)
return &baseInfo, nil
}
func (u *DashboardService) LoadCurrentInfo(ioOption string, netOption string) *dto.DashboardCurrent {
var currentInfo dto.DashboardCurrent
hostInfo, _ := host.Info()
currentInfo.Procs = hostInfo.Procs
currentInfo.CPUTotal, _ = cpu.Counts(true)
totalPercent, _ := cpu.Percent(0, false)
if len(totalPercent) == 1 {
currentInfo.CPUUsedPercent = totalPercent[0]
currentInfo.CPUUsed = currentInfo.CPUUsedPercent * 0.01 * float64(currentInfo.CPUTotal)
}
currentInfo.CPUPercent, _ = cpu.Percent(0, true)
loadInfo, _ := load.Avg()
currentInfo.Load1 = loadInfo.Load1
currentInfo.Load5 = loadInfo.Load5
currentInfo.Load15 = loadInfo.Load15
currentInfo.LoadUsagePercent = loadInfo.Load1 / (float64(currentInfo.CPUTotal*2) * 0.75) * 100
memoryInfo, _ := mem.VirtualMemory()
currentInfo.MemoryTotal = memoryInfo.Total
currentInfo.MemoryAvailable = memoryInfo.Available
currentInfo.MemoryUsed = memoryInfo.Used
currentInfo.MemoryUsedPercent = memoryInfo.UsedPercent
state, _ := disk.Usage("/")
currentInfo.Total = state.Total
currentInfo.Free = state.Free
currentInfo.Used = state.Used
currentInfo.UsedPercent = state.UsedPercent
currentInfo.InodesTotal = state.InodesTotal
currentInfo.InodesUsed = state.InodesUsed
currentInfo.InodesFree = state.InodesFree
currentInfo.InodesUsedPercent = state.InodesUsedPercent
if ioOption == "all" {
diskInfo, _ := disk.IOCounters()
for _, state := range diskInfo {
currentInfo.IOReadBytes += state.ReadBytes
currentInfo.IOWriteBytes += state.WriteBytes
currentInfo.IOCount += (state.ReadCount + state.WriteCount)
currentInfo.IOTime += state.ReadTime / 1000 / 1000
if state.WriteTime > state.ReadTime {
currentInfo.IOTime += state.WriteTime / 1000 / 1000
}
}
} else {
diskInfo, _ := disk.IOCounters(ioOption)
for _, state := range diskInfo {
currentInfo.IOReadBytes += state.ReadBytes
currentInfo.IOWriteBytes += state.WriteBytes
currentInfo.IOTime += state.ReadTime / 1000 / 1000
if state.WriteTime > state.ReadTime {
currentInfo.IOTime += state.WriteTime / 1000 / 1000
}
}
}
if netOption == "all" {
netInfo, _ := net.IOCounters(false)
if len(netInfo) != 0 {
currentInfo.NetBytesSent = netInfo[0].BytesSent
currentInfo.NetBytesRecv = netInfo[0].BytesRecv
}
} else {
netInfo, _ := net.IOCounters(true)
for _, state := range netInfo {
if state.Name == netOption {
currentInfo.NetBytesSent = state.BytesSent
currentInfo.NetBytesRecv = state.BytesRecv
}
}
}
currentInfo.ShotTime = time.Now()
return &currentInfo
}

View File

@ -4,6 +4,7 @@ import "github.com/1Panel-dev/1Panel/backend/app/repo"
type ServiceGroup struct {
AuthService
DashboardService
AppService
AppInstallService

View File

@ -67,6 +67,7 @@ func Routers() *gin.Engine {
// PrivateGroup.Use(middleware.SafetyAuth())
{
systemRouter.InitBaseRouter(PrivateGroup)
systemRouter.InitDashboardRouter(PrivateGroup)
systemRouter.InitHostRouter(PrivateGroup)
systemRouter.InitBackupRouter(PrivateGroup)
systemRouter.InitGroupRouter(PrivateGroup)

View File

@ -20,6 +20,7 @@ func PasswordExpired() gin.HandlerFunc {
expiredDays, _ := strconv.Atoi(setting.Value)
if expiredDays == 0 {
c.Next()
return
}
if _, err := c.Cookie(constant.PasswordExpiredName); err != nil {

View File

@ -2,6 +2,7 @@ package router
type RouterGroup struct {
BaseRouter
DashboardRouter
HostRouter
BackupRouter
GroupRouter

View File

@ -0,0 +1,22 @@
package router
import (
v1 "github.com/1Panel-dev/1Panel/backend/app/api/v1"
"github.com/1Panel-dev/1Panel/backend/middleware"
"github.com/gin-gonic/gin"
)
type DashboardRouter struct{}
func (s *CronjobRouter) InitDashboardRouter(Router *gin.RouterGroup) {
cmdRouter := Router.Group("dashboard").
Use(middleware.JwtAuth()).
Use(middleware.SessionAuth()).
Use(middleware.PasswordExpired())
baseApi := v1.ApiGroupApp.BaseApi
{
cmdRouter.GET("/base/:ioOption/:netOption", baseApi.LoadDashboardBaseInfo)
cmdRouter.GET("/current/:ioOption/:netOption", baseApi.LoadDashboardCurrentInfo)
}
}

View File

@ -18,5 +18,6 @@ func (s *MonitorRouter) InitMonitorRouter(Router *gin.RouterGroup) {
{
monitorRouter.POST("/search", baseApi.LoadMonitor)
monitorRouter.GET("/netoptions", baseApi.GetNetworkOptions)
monitorRouter.GET("/iooptions", baseApi.GetIOOptions)
}
}

View File

@ -0,0 +1,68 @@
export namespace Dashboard {
export interface BaseInfo {
haloID: number;
dateeaseID: number;
jumpserverID: number;
metersphereID: number;
kubeoperatorID: number;
kubepiID: number;
websiteNumber: number;
databaseNumber: number;
cronjobNumber: number;
appInstalldNumber: number;
hostname: string;
os: string;
platform: string;
platformFamily: string;
platformVersion: string;
kernelArch: string;
kernelVersion: string;
virtualizationSystem: string;
cpuCores: number;
cpuLogicalCores: number;
cpuModelName: string;
currentInfo: CurrentInfo;
}
export interface CurrentInfo {
procs: number;
load1: number;
load5: number;
load15: number;
loadUsagePercent: number;
cpuPercent: Array<number>;
cpuUsedPercent: number;
cpuUsed: number;
cpuTotal: number;
memoryTotal: number;
memoryAvailable: number;
memoryUsed: number;
MemoryUsedPercent: number;
ioReadBytes: number;
ioWriteBytes: number;
ioTime: number;
ioCount: number;
total: number;
free: number;
used: number;
usedPercent: number;
inodesTotal: number;
inodesUsed: number;
inodesFree: number;
inodesUsedPercent: number;
netBytesSent: number;
netBytesRecv: number;
shotTime: Date;
}
}

View File

@ -0,0 +1,10 @@
import http from '@/api';
import { Dashboard } from '../interface/dashboard';
export const loadBaseInfo = (ioOption: string, netOption: string) => {
return http.get<Dashboard.BaseInfo>(`/dashboard/base/${ioOption}/${netOption}`);
};
export const loadCurrentInfo = (ioOption: string, netOption: string) => {
return http.get<Dashboard.CurrentInfo>(`/dashboard/current/${ioOption}/${netOption}`);
};

View File

@ -8,3 +8,7 @@ export const loadMonitor = (param: Monitor.MonitorSearch) => {
export const getNetworkOptions = () => {
return http.get<Array<string>>(`/monitors/netoptions`);
};
export const getIOOptions = () => {
return http.get<Array<string>>(`/monitors/iooptions`);
};

View File

@ -147,7 +147,38 @@ export default {
logs: 'Log',
},
home: {
welcome: 'Welcome',
overview: 'Overview',
appInstalled: 'App installed',
systemInfo: 'System info',
hostname: 'Hostname',
platformVersion: 'Platform version',
kernelVersion: 'Kernel version',
kernelArch: 'Kernel arch',
network: 'Network',
io: 'Disk IO',
totalSend: 'Total send',
totalRecv: 'Total recv',
rwPerSecond: 'RW per second',
ioDelay: 'IO delay',
time: 'Times',
core: 'Physical core',
logicCore: 'Logic core',
loadAverage: 'Average load in the last {0} minutes',
load: 'Load',
mount: 'Mount point',
total: 'Total',
used: 'Used',
free: 'Free',
percent: 'Percent',
app: 'Recommended Apps',
haloInfo: 'Easy to use and powerful open source website tools',
deInfo: 'Open source data visualization tool available to everyone',
jsInfo: 'The popular Open source fortress machine',
msInfo: 'One-stop open source continuous testing platform',
koInfo: 'Open source lightweight Kubernetes distribution',
kubepiInfo: 'Modern open source K8s panel',
goInstall: 'Go install',
},
tabs: {
more: 'More',

View File

@ -148,7 +148,38 @@ export default {
logs: '面板日志',
},
home: {
welcome: '欢迎使用',
overview: ' ',
appInstalled: '已安装应用',
systemInfo: '系统信息',
hostname: '主机名称',
platformVersion: '发行版本',
kernelVersion: '内核版本',
kernelArch: '系统类型',
network: '流量',
io: '磁盘 IO',
totalSend: '总发送',
totalRecv: '总接收',
rwPerSecond: '每秒读写',
ioDelay: 'IO 延迟',
time: '次',
core: '物理核心',
logicCore: '逻辑核心',
loadAverage: '最近 {0} 分钟平均负载',
load: '负载',
mount: '挂载点',
total: '总数',
used: '已用',
free: '可用',
percent: '使用率',
app: '推荐应用',
haloInfo: '好用又强大的开源建站工具',
deInfo: '人人可用的开源数据可视化分析工具',
jsInfo: '广受欢迎的开源堡垒机',
msInfo: '一站式开源持续测试平台',
koInfo: '开源的轻量级 Kubernetes 发行版',
kubepiInfo: '现代化的开源 K8s 面板',
goInstall: '去安装',
},
tabs: {
more: '更多',

View File

@ -1,57 +1,64 @@
<template>
<div>
<Submenu activeName="mysql" />
<AppStatus :app-key="'mysql'" style="margin-top: 20px" @setting="onSetting"></AppStatus>
<Setting ref="settingRef"></Setting>
<AppStatus :app-key="'mysql'" style="margin-top: 20px" @setting="onSetting" @is-exist="checkExist" />
<div v-if="mysqlIsExist">
<Setting ref="settingRef"></Setting>
<el-card v-if="!isOnSetting" style="margin-top: 5px">
<ComplexTable :pagination-config="paginationConfig" v-model:selects="selects" @search="search" :data="data">
<template #toolbar>
<el-button type="primary" @click="onOpenDialog()">{{ $t('commons.button.create') }}</el-button>
<el-button>phpMyAdmin</el-button>
<el-button type="danger" plain :disabled="selects.length === 0" @click="onBatchDelete(null)">
{{ $t('commons.button.delete') }}
</el-button>
</template>
<el-table-column type="selection" fix />
<el-table-column :label="$t('commons.table.name')" prop="name" />
<el-table-column :label="$t('commons.login.username')" prop="username" />
<el-table-column :label="$t('commons.login.password')" prop="password">
<template #default="{ row }">
<div v-if="!row.showPassword">
<span style="float: left">***********</span>
<div style="margin-top: 2px; cursor: pointer">
<el-icon style="margin-left: 5px" @click="row.showPassword = true" :size="16">
<View />
</el-icon>
</div>
</div>
<div v-else>
<span style="float: left">{{ row.password }}</span>
<div style="margin-top: 4px; cursor: pointer">
<el-icon style="margin-left: 5px" @click="row.showPassword = false" :size="16">
<Hide />
</el-icon>
</div>
</div>
<el-card v-if="!isOnSetting" style="margin-top: 5px">
<ComplexTable
:pagination-config="paginationConfig"
v-model:selects="selects"
@search="search"
:data="data"
>
<template #toolbar>
<el-button type="primary" @click="onOpenDialog()">{{ $t('commons.button.create') }}</el-button>
<el-button>phpMyAdmin</el-button>
<el-button type="danger" plain :disabled="selects.length === 0" @click="onBatchDelete(null)">
{{ $t('commons.button.delete') }}
</el-button>
</template>
</el-table-column>
<el-table-column :label="$t('commons.table.description')" prop="description" />
<el-table-column
prop="createdAt"
:label="$t('commons.table.date')"
:formatter="dateFromat"
show-overflow-tooltip
/>
<fu-table-operations
width="300px"
:buttons="buttons"
:ellipsis="10"
:label="$t('commons.table.operate')"
fix
/>
</ComplexTable>
</el-card>
<el-table-column type="selection" fix />
<el-table-column :label="$t('commons.table.name')" prop="name" />
<el-table-column :label="$t('commons.login.username')" prop="username" />
<el-table-column :label="$t('commons.login.password')" prop="password">
<template #default="{ row }">
<div v-if="!row.showPassword">
<span style="float: left">***********</span>
<div style="margin-top: 2px; cursor: pointer">
<el-icon style="margin-left: 5px" @click="row.showPassword = true" :size="16">
<View />
</el-icon>
</div>
</div>
<div v-else>
<span style="float: left">{{ row.password }}</span>
<div style="margin-top: 4px; cursor: pointer">
<el-icon style="margin-left: 5px" @click="row.showPassword = false" :size="16">
<Hide />
</el-icon>
</div>
</div>
</template>
</el-table-column>
<el-table-column :label="$t('commons.table.description')" prop="description" />
<el-table-column
prop="createdAt"
:label="$t('commons.table.date')"
:formatter="dateFromat"
show-overflow-tooltip
/>
<fu-table-operations
width="300px"
:buttons="buttons"
:ellipsis="10"
:label="$t('commons.table.operate')"
fix
/>
</ComplexTable>
</el-card>
</div>
<el-dialog v-model="changeVisiable" :destroy-on-close="true" width="30%">
<template #header>
<div class="card-header">
@ -122,6 +129,7 @@ import { useDeleteData } from '@/hooks/use-delete-data';
import { ElForm, ElMessage } from 'element-plus';
import { Database } from '@/api/interface/database';
import { Rules } from '@/global/form-rules';
import { App } from '@/api/interface/app';
const selects = ref<any>([]);
const mysqlInfo = reactive({
@ -138,6 +146,8 @@ const paginationConfig = reactive({
total: 0,
});
const mysqlIsExist = ref(false);
const dialogRef = ref();
const onOpenDialog = async () => {
let params = {
@ -202,6 +212,10 @@ const search = async () => {
paginationConfig.total = res.data.total;
};
const checkExist = (data: App.CheckInstalled) => {
mysqlIsExist.value = data.isExist;
};
const onBatchDelete = async (row: Database.MysqlDBInfo | null) => {
let ids: Array<number> = [];
if (row) {

View File

@ -2,10 +2,12 @@
<div>
<Submenu activeName="redis" />
<AppStatus :app-key="'redis'" style="margin-top: 20px" @setting="onSetting"></AppStatus>
<Setting ref="settingRef"></Setting>
<AppStatus :app-key="'redis'" style="margin-top: 20px" @setting="onSetting" @is-exist="checkExist"></AppStatus>
<div v-show="redisIsExist">
<Setting ref="settingRef"></Setting>
<Terminal v-if="!isOnSetting" style="margin-top: 5px" ref="terminalRef"></Terminal>
<Terminal v-show="!isOnSetting" style="margin-top: 5px" ref="terminalRef"></Terminal>
</div>
</div>
</template>
@ -14,11 +16,13 @@ import Submenu from '@/views/database/index.vue';
import Setting from '@/views/database/redis/setting/index.vue';
import Terminal from '@/views/database/redis/terminal/index.vue';
import AppStatus from '@/components/app-status/index.vue';
import { onMounted, ref } from 'vue';
import { ref } from 'vue';
import { App } from '@/api/interface/app';
const terminalRef = ref();
const settingRef = ref();
const isOnSetting = ref(false);
const redisIsExist = ref(false);
const onSetting = async () => {
isOnSetting.value = true;
@ -26,7 +30,10 @@ const onSetting = async () => {
settingRef.value!.acceptParams();
};
onMounted(() => {
terminalRef.value.acceptParams();
});
const checkExist = (data: App.CheckInstalled) => {
redisIsExist.value = data.isExist;
if (redisIsExist.value) {
terminalRef.value.acceptParams();
}
};
</script>

View File

@ -0,0 +1,161 @@
<template>
<div>
<el-card class="el-card">
<template #header>
<div class="card-header">
<span>{{ $t('home.app') }}</span>
</div>
</template>
<el-row :gutter="20">
<el-col :span="12">
<div @click="goInstall(baseInfo.haloID)">
<el-card style="height: 110px" @click="goInstall(baseInfo.haloID)">
<el-row>
<el-col :span="4">
<img style="width: 40px; height: 40px" src="../images/halo.jpg" alt="" />
</el-col>
<el-col :span="2"><br /></el-col>
<el-col :span="18">
<span style="font-size: 20px; color: #214bc8">Halo</span>
<div>
<span class="input-help">{{ $t('home.haloInfo') }}</span>
</div>
</el-col>
</el-row>
</el-card>
</div>
</el-col>
<el-col :span="12">
<div @click="goInstall(baseInfo.dateeaseID)">
<el-card style="height: 110px" @click="goInstall(baseInfo.dateeaseID)">
<el-row>
<el-col :span="4">
<img style="width: 40px; height: 40px" src="../images/de.jpg" alt="" />
</el-col>
<el-col :span="2"><br /></el-col>
<el-col :span="18">
<span style="font-size: 20px; color: #0070d6">Dataease</span>
<div>
<span class="input-help">{{ $t('home.deInfo') }}</span>
</div>
</el-col>
</el-row>
</el-card>
</div>
</el-col>
</el-row>
<el-row :gutter="20" style="margin-top: 20px">
<el-col :span="12">
<div @click="goInstall(baseInfo.jumpserverID)">
<el-card style="height: 110px" @click="goInstall(baseInfo.jumpserverID)">
<el-row>
<el-col :span="4">
<img style="width: 40px; height: 40px" src="../images/js.jpg" alt="" />
</el-col>
<el-col :span="2"><br /></el-col>
<el-col :span="18">
<span style="font-size: 16px; color: #008d75">JumpServer</span>
<div>
<span class="input-help">{{ $t('home.jsInfo') }}</span>
</div>
</el-col>
</el-row>
</el-card>
</div>
</el-col>
<el-col :span="12">
<div @click="goInstall(baseInfo.metersphereID)">
<el-card style="height: 110px" @click="goInstall(baseInfo.metersphereID)">
<el-row>
<el-col :span="4">
<img style="width: 40px; height: 40px" src="../images/ms.jpg" alt="" />
</el-col>
<el-col :span="2"><br /></el-col>
<el-col :span="18">
<span style="font-size: 16px; color: #723279">MeterSphere</span>
<div>
<span class="input-help">{{ $t('home.msInfo') }}</span>
</div>
</el-col>
</el-row>
</el-card>
</div>
</el-col>
</el-row>
<el-row :gutter="20" style="margin-top: 20px">
<el-col :span="12">
<div @click="goInstall(baseInfo.kubeoperatorID)">
<el-card style="height: 110px" @click="goInstall(baseInfo.kubeoperatorID)">
<el-row>
<el-col :span="4">
<img style="width: 40px; height: 40px" src="../images/ko.jpg" alt="" />
</el-col>
<el-col :span="2"><br /></el-col>
<el-col :span="18">
<span style="font-size: 16px; color: #fc3535">KubeOperator</span>
<div>
<span class="input-help">{{ $t('home.koInfo') }}</span>
</div>
</el-col>
</el-row>
</el-card>
</div>
</el-col>
<el-col :span="12">
<div @click="goInstall(baseInfo.kubepiID)">
<el-card style="height: 110px" @click="goInstall(baseInfo.kubepiID)">
<el-row>
<el-col :span="4">
<img style="width: 40px; height: 40px" src="../images/kubepi.jpg" alt="" />
</el-col>
<el-col :span="2"><br /></el-col>
<el-col :span="18">
<span style="font-size: 16px; color: #fc3535">KubePi</span>
<div>
<span class="input-help">{{ $t('home.kubepiInfo') }}</span>
</div>
</el-col>
</el-row>
</el-card>
</div>
</el-col>
</el-row>
</el-card>
</div>
</template>
<script lang="ts" setup>
import { Dashboard } from '@/api/interface/dashboard';
import { ref } from 'vue';
import { useRouter } from 'vue-router';
const router = useRouter();
const baseInfo = ref({
haloID: 0,
dateeaseID: 0,
jumpserverID: 0,
metersphereID: 0,
kubeoperatorID: 0,
kubepiID: 0,
});
const acceptParams = (base: Dashboard.BaseInfo): void => {
baseInfo.value.haloID = base.haloID;
baseInfo.value.dateeaseID = base.dateeaseID;
baseInfo.value.jumpserverID = base.jumpserverID;
baseInfo.value.metersphereID = base.metersphereID;
baseInfo.value.kubeoperatorID = base.kubeoperatorID;
baseInfo.value.kubepiID = base.kubepiID;
};
const goInstall = (id: number) => {
let params: { [key: string]: any } = {
id: id,
};
router.push({ name: 'AppDetail', params });
};
defineExpose({
acceptParams,
});
</script>

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -5,33 +5,37 @@
<el-card class="el-card">
<template #header>
<div class="card-header">
<span> </span>
<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">网站</span>
<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"
>
2
{{ 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">数据库</span>
<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"
>
5
{{ baseInfo?.databaseNumber }}
</el-link>
</el-card>
</el-col>
@ -40,26 +44,30 @@
<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">定时任务</span>
<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"
>
7
{{ 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">已安装应用</span>
<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"
>
3
{{ baseInfo?.appInstalldNumber }}
</el-link>
</el-card>
</el-col>
@ -67,194 +75,201 @@
</el-card>
</el-col>
<el-col :span="8">
<el-card class="el-card">
<template #header>
<div class="card-header">
<span>状态</span>
</div>
</template>
<el-row :gutter="10">
<el-col :span="12" align="center">
<el-progress type="dashboard" :width="80" :percentage="80">
<template #default="{ percentage }">
<span class="percentage-value">{{ percentage }}%</span>
<span class="percentage-label">CPU</span>
</template>
</el-progress>
<br />
<span>(0.56 / 8.00) Core</span>
</el-col>
<el-col :span="12" align="center">
<el-progress type="dashboard" :width="80" :percentage="30">
<template #default="{ percentage }">
<span class="percentage-value">{{ percentage }}%</span>
<span class="percentage-label">内存</span>
</template>
</el-progress>
<br />
<span>(851 / 7812) MB</span>
</el-col>
</el-row>
<el-row :gutter="10" style="margin-top: 30px">
<el-col :span="12" align="center">
<el-progress type="dashboard" :width="80" :percentage="50">
<template #default="{ percentage }">
<span class="percentage-value">{{ percentage }}%</span>
<span class="percentage-label">负载</span>
</template>
</el-progress>
<br />
<span>(0.36 / 8.00) Core</span>
</el-col>
<el-col :span="12" align="center">
<el-progress type="dashboard" :width="80" :percentage="40">
<template #default="{ percentage }">
<span class="percentage-value">{{ percentage }}%</span>
<span class="percentage-label">磁盘</span>
</template>
</el-progress>
<br />
<span>(6.23 / 46.97) GB</span>
</el-col>
</el-row>
</el-card>
<Status ref="statuRef" />
</el-col>
<el-col :span="8">
<el-card class="el-card">
<template #header>
<div class="card-header">
<span>系统信息</span>
<span>{{ $t('home.systemInfo') }}</span>
</div>
</template>
<el-form>
<el-form-item label="主机名称">ko-deploy</el-form-item>
<el-form-item label="发行版本">centos-7.6.1810</el-form-item>
<el-form-item label="内核版本">
Linux version 3.10.0-957.el7.x86_64 (mockbuild@kbuilder.bsys.centos.org) (gcc version 4.8.5
20150623 (Red Hat 4.8.5-36) (GCC) ) #1 SMP Thu Nov 8 23:39:32 UTC 2018
<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="系统类型">x86_64</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>
</el-card>
</el-col>
</el-row>
<el-row :gutter="20" style="margin-top: 20px" class="row-box">
<el-col :span="12">
<el-card class="el-card">
<template #header>
<div class="card-header">
<span>应用</span>
</div>
</template>
<el-row :gutter="20">
<el-col :span="12">
<el-card @mouseover="hoverApp = 'halo'" @mouseleave="hoverApp = ''" style="height: 110px">
<el-row>
<el-col :span="4">
<img style="width: 40px; height: 40px" src="./images/halo.jpg" alt="" />
</el-col>
<el-col :span="2"><br /></el-col>
<el-col :span="18">
<span style="font-size: 20px; color: #214bc8">Halo</span>
<div><span class="input-help">现代化开源建站 / CMS系统</span></div>
</el-col>
</el-row>
<div v-show="hoverApp === 'halo'" style="float: right; margin-top: 10px">
<el-button>停止</el-button>
<el-button>重启</el-button>
</div>
</el-card>
</el-col>
<el-col :span="12">
<el-card @mouseover="hoverApp = 'de'" @mouseleave="hoverApp = ''" style="height: 110px">
<el-row>
<el-col :span="4">
<img style="width: 40px; height: 40px" src="./images/de.jpg" alt="" />
</el-col>
<el-col :span="2"><br /></el-col>
<el-col :span="18">
<span style="font-size: 20px; color: #0070d6">Dataease</span>
<div><span class="input-help">开源数据可视化分析工具</span></div>
</el-col>
</el-row>
<div v-show="hoverApp === 'de'" style="float: right; margin-top: 10px">
<el-button>启动</el-button>
</div>
</el-card>
</el-col>
</el-row>
<el-row :gutter="20" style="margin-top: 20px">
<el-col :span="12">
<el-card @mouseover="hoverApp = 'js'" @mouseleave="hoverApp = ''" style="height: 110px">
<el-row>
<el-col :span="4">
<img style="width: 40px; height: 40px" src="./images/js.jpg" alt="" />
</el-col>
<el-col :span="2"><br /></el-col>
<el-col :span="18">
<span style="font-size: 16px; color: #008d75">JumpServer</span>
<div><span class="input-help">广受欢迎的开源堡垒机</span></div>
</el-col>
</el-row>
<div v-show="hoverApp === 'js'" style="float: right; margin-top: 10px">
<el-button>停止</el-button>
<el-button>重启</el-button>
</div>
</el-card>
</el-col>
<el-col :span="12">
<el-card @mouseover="hoverApp = 'ms'" @mouseleave="hoverApp = ''" style="height: 110px">
<el-row>
<el-col :span="4">
<img style="width: 40px; height: 40px" src="./images/ms.jpg" alt="" />
</el-col>
<el-col :span="2"><br /></el-col>
<el-col :span="18">
<span style="font-size: 16px; color: #723279">MeterSphere</span>
<div><span class="input-help">一站式开源持续测试平台</span></div>
</el-col>
</el-row>
<div v-show="hoverApp === 'ms'" style="float: right; margin-top: 10px">
<el-button>启动</el-button>
</div>
</el-card>
</el-col>
</el-row>
</el-card>
<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="io">流量</el-radio-button>
<el-radio-button label="network">网络</el-radio-button>
<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>
<div v-if="chartOption === 'io'" id="ioChart" style="width: 100%; height: 320px"></div>
<div v-if="chartOption === 'network'" id="networkChart" style="width: 100%; height: 320px"></div>
<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 setup lang="ts" name="dataVisualize">
import { onMounted, onBeforeUnmount, ref } from 'vue';
<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 { ContainerStats } from '@/api/modules/container';
import { dateFromatForSecond } from '@/utils/util';
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 hoverApp = ref();
const chartOption = ref('io');
const statuRef = ref();
const appRef = ref();
const chartOption = ref('network');
let timer: NodeJS.Timer | null = null;
let isInit = ref<boolean>(true);
const ioReadDatas = ref<Array<string>>([]);
const ioWriteDatas = ref<Array<string>>([]);
const netTxDatas = ref<Array<string>>([]);
const netRxDatas = ref<Array<string>>([]);
const timeDatas = ref<Array<string>>([]);
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: '',
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;
@ -265,92 +280,151 @@ 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;
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 () => {
try {
const res = await ContainerStats('kubeoperator_nginx');
ioReadDatas.value.push(res.data.ioRead.toFixed(2));
if (ioReadDatas.value.length > 20) {
ioReadDatas.value.splice(0, 1);
}
ioWriteDatas.value.push(res.data.ioWrite.toFixed(2));
if (ioWriteDatas.value.length > 20) {
ioWriteDatas.value.splice(0, 1);
}
netTxDatas.value.push(res.data.networkTX.toFixed(2));
if (netTxDatas.value.length > 20) {
netTxDatas.value.splice(0, 1);
}
netRxDatas.value.push(res.data.networkRX.toFixed(2));
if (netRxDatas.value.length > 20) {
netRxDatas.value.splice(0, 1);
}
timeDatas.value.push(dateFromatForSecond(res.data.shotTime));
if (timeDatas.value.length > 20) {
timeDatas.value.splice(0, 1);
}
if (chartOption.value === 'io') {
let ioReadYDatas = {
name: i18n.global.t('monitor.read'),
type: 'line',
areaStyle: {
color: '#ebdee3',
},
data: ioReadDatas.value,
showSymbol: false,
};
let ioWriteYDatas = {
name: i18n.global.t('monitor.write'),
type: 'line',
areaStyle: {
color: '#ebdee3',
},
data: ioWriteDatas.value,
showSymbol: false,
};
freshChart(
'ioChart',
[i18n.global.t('monitor.read'), i18n.global.t('monitor.write')],
timeDatas.value,
[ioReadYDatas, ioWriteYDatas],
'流量',
'MB',
);
} else {
let netTxYDatas = {
name: i18n.global.t('monitor.up'),
type: 'line',
areaStyle: {
color: '#ebdee3',
},
data: netTxDatas.value,
showSymbol: false,
};
let netRxYDatas = {
name: i18n.global.t('monitor.down'),
type: 'line',
areaStyle: {
color: '#ebdee3',
},
data: netRxDatas.value,
showSymbol: false,
};
freshChart(
'networkChart',
[i18n.global.t('monitor.up'), i18n.global.t('monitor.down')],
timeDatas.value,
[netTxYDatas, netRxYDatas],
i18n.global.t('monitor.network'),
'KB/s',
);
}
} catch {
clearInterval(Number(timer));
timer = null;
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],
'流量',
'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) {
console.log(chartName, echarts.getInstanceByDom(document.getElementById(chartName) as HTMLElement));
if (isInit.value) {
echarts.init(document.getElementById(chartName) as HTMLElement);
isInit.value = false;
@ -393,11 +467,9 @@ function changeChartSize() {
}
onMounted(() => {
loadData();
window.addEventListener('resize', changeChartSize);
timer = setInterval(async () => {
loadData();
}, 3000);
onLoadNetworkOptions();
onLoadIOOptions();
onLoadBaseInfo(true, 'all');
});
onBeforeUnmount(() => {
@ -408,15 +480,6 @@ onBeforeUnmount(() => {
</script>
<style scoped>
.percentage-value {
display: block;
font-size: 16px;
}
.percentage-label {
display: block;
margin-top: 10px;
font-size: 12px;
}
.card-header {
display: flex;
justify-content: space-between;

View File

@ -0,0 +1,236 @@
<template>
<div>
<el-card class="el-card">
<template #header>
<div class="card-header">
<span>{{ $t('commons.table.status') }}</span>
</div>
</template>
<el-row :gutter="10">
<el-col :span="12" align="center">
<el-popover placement="bottom" :width="300" trigger="hover">
<div style="margin-bottom: 10px">
<el-tag>{{ baseInfo.cpuModelName }}</el-tag>
</div>
<el-tag>
{{ $t('home.core') }} *{{ baseInfo.cpuCores }} {{ $t('home.logicCore') }} *{{
baseInfo.cpuLogicalCores
}}
</el-tag>
<br />
<el-tag style="margin-top: 5px" v-for="(item, index) of currentInfo.cpuPercent" :key="index">
CPU-{{ index }}: {{ formatNumber(item) }}%
</el-tag>
<template #reference>
<el-progress
type="dashboard"
:width="80"
:percentage="formatNumber(currentInfo.cpuUsedPercent)"
>
<template #default="{ percentage }">
<span class="percentage-value">{{ percentage }}%</span>
<span class="percentage-label">CPU</span>
</template>
</el-progress>
</template>
</el-popover>
<br />
<span>( {{ formatNumber(currentInfo.cpuUsed) }} / {{ currentInfo.cpuTotal }} ) Core</span>
</el-col>
<el-col :span="12" align="center">
<el-progress type="dashboard" :width="80" :percentage="formatNumber(currentInfo.MemoryUsedPercent)">
<template #default="{ percentage }">
<span class="percentage-value">{{ percentage }}%</span>
<span class="percentage-label">{{ $t('monitor.memory') }}</span>
</template>
</el-progress>
<br />
<span>
( {{ formatNumber(currentInfo.memoryUsed / 1024 / 1024) }} /
{{ currentInfo.memoryTotal / 1024 / 1024 }} ) MB
</span>
</el-col>
</el-row>
<el-row :gutter="10" style="margin-top: 30px">
<el-col :span="12" align="center">
<el-popover placement="bottom" :width="200" trigger="hover">
<el-tag style="margin-top: 5px">
{{ $t('home.loadAverage', [1]) }}: {{ formatNumber(currentInfo.load1) }}
</el-tag>
<el-tag style="margin-top: 5px">
{{ $t('home.loadAverage', [5]) }}: {{ formatNumber(currentInfo.load5) }}
</el-tag>
<el-tag style="margin-top: 5px">
{{ $t('home.loadAverage', [15]) }}: {{ formatNumber(currentInfo.load15) }}
</el-tag>
<template #reference>
<el-progress
type="dashboard"
:width="80"
:percentage="formatNumber(currentInfo.loadUsagePercent)"
>
<template #default="{ percentage }">
<span class="percentage-value">{{ percentage }}%</span>
<span class="percentage-label">{{ $t('home.load') }}</span>
</template>
</el-progress>
</template>
</el-popover>
<br />
<span>{{ loadStatus(currentInfo.loadUsagePercent) }}</span>
</el-col>
<el-col :span="12" align="center">
<el-popover placement="bottom" :width="160" trigger="hover">
<el-tag>{{ $t('home.mount') }}: /</el-tag>
<div><el-tag style="margin-top: 10px">iNode</el-tag></div>
<el-tag style="margin-top: 5px">{{ $t('home.total') }}: {{ currentInfo.inodesTotal }}</el-tag>
<el-tag style="margin-top: 3px">{{ $t('home.used') }}: {{ currentInfo.inodesUsed }}</el-tag>
<el-tag style="margin-top: 3px">{{ $t('home.free') }}: {{ currentInfo.inodesFree }}</el-tag>
<el-tag style="margin-top: 3px">
{{ $t('home.percent') }}: {{ formatNumber(currentInfo.inodesUsedPercent) }}%
</el-tag>
<div>
<el-tag style="margin-top: 10px">{{ $t('monitor.disk') }}</el-tag>
</div>
<el-tag style="margin-top: 5px">
{{ $t('home.total') }}: {{ formatNumber(currentInfo.total / 1024 / 1024 / 1024) }} GB
</el-tag>
<el-tag style="margin-top: 3px">
{{ $t('home.used') }}: {{ formatNumber(currentInfo.used / 1024 / 1024 / 1024) }} GB
</el-tag>
<el-tag style="margin-top: 3px">
{{ $t('home.free') }}: {{ formatNumber(currentInfo.free / 1024 / 1024 / 1024) }} GB
</el-tag>
<el-tag style="margin-top: 3px">
{{ $t('home.percent') }}: {{ formatNumber(currentInfo.usedPercent) }}%
</el-tag>
<template #reference>
<el-progress
type="dashboard"
:width="80"
:percentage="formatNumber(currentInfo.usedPercent)"
>
<template #default="{ percentage }">
<span class="percentage-value">{{ percentage }}%</span>
<span class="percentage-label">{{ $t('monitor.disk') }}</span>
</template>
</el-progress>
</template>
</el-popover>
<br />
<span>
( {{ formatNumber(currentInfo.used / 1024 / 1024 / 1024) }} /
{{ formatNumber(currentInfo.total / 1024 / 1024 / 1024) }} ) GB
</span>
</el-col>
</el-row>
</el-card>
</div>
</template>
<script lang="ts" setup>
import { Dashboard } from '@/api/interface/dashboard';
import { ref } from 'vue';
const baseInfo = ref<Dashboard.BaseInfo>({
haloInstatllID: 0,
dateeaseInstatllID: 0,
jumpserverInstatllID: 0,
metersphereInstatllID: 0,
websiteNumber: 0,
databaseNumber: 0,
cronjobNumber: 0,
appInstalldNumber: 0,
hostname: '',
os: '',
platform: '',
platformFamily: '',
platformVersion: '',
kernelArch: '',
kernelVersion: '',
virtualizationSystem: '',
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 acceptParams = (current: Dashboard.CurrentInfo, base: Dashboard.BaseInfo): void => {
currentInfo.value = current;
baseInfo.value = base;
};
function formatNumber(val: number) {
return Number(val.toFixed(2));
}
function loadStatus(val: number) {
if (val < 30) {
return '运行流畅';
}
if (val < 70) {
return '运行正常';
}
if (val < 80) {
return '运行缓慢';
}
return '运行堵塞';
}
defineExpose({
acceptParams,
});
</script>
<style scoped>
.percentage-value {
display: block;
font-size: 16px;
}
.percentage-label {
display: block;
margin-top: 10px;
font-size: 12px;
}
</style>

6
go.sum
View File

@ -554,6 +554,7 @@ github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtng
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM=
github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-retryablehttp v0.7.1 h1:sUiuQAnLlbvmExtFQs72iFW/HXeUn8Z1aJLQ4LJJbTQ=
@ -673,8 +674,8 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
@ -845,14 +846,15 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=
github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY=
github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=