fix
This commit is contained in:
@@ -119,6 +119,11 @@ export interface HostMetricsDiskMount {
|
|||||||
export interface HostMetricsCpuCard {
|
export interface HostMetricsCpuCard {
|
||||||
usage_percent: number
|
usage_percent: number
|
||||||
logical_cores_total: number
|
logical_cores_total: number
|
||||||
|
socket_count?: number
|
||||||
|
physical_cores_total?: number
|
||||||
|
physical_threads_total?: number
|
||||||
|
cores_per_socket?: number
|
||||||
|
threads_per_core?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface HostMetricsSummary {
|
export interface HostMetricsSummary {
|
||||||
@@ -163,3 +168,82 @@ export const fetchServerNetworkTraffic = (serverIdentity: string, hours = 6) =>
|
|||||||
{ params: { server_identity: serverIdentity, hours } },
|
{ params: { server_identity: serverIdentity, hours } },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 虚拟机资产概览 */
|
||||||
|
export interface VirtualOverviewPayload {
|
||||||
|
vm_total: number
|
||||||
|
asset_group_total: number
|
||||||
|
online_total: number
|
||||||
|
latest_timestamp?: string
|
||||||
|
latest_cpu_usage_avg?: number
|
||||||
|
latest_mem_usage_avg?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 物理机 24h 趋势点 */
|
||||||
|
export interface PhysicalUsagePoint {
|
||||||
|
hour: string
|
||||||
|
cpu_used_percent_avg: number | null
|
||||||
|
mem_used_percent_avg: number | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PhysicalUsageTrendPayload {
|
||||||
|
hours: number
|
||||||
|
points: PhysicalUsagePoint[]
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 资源总量项 */
|
||||||
|
export interface ResourceTotalGroup {
|
||||||
|
server_type: 'physical' | 'virtual'
|
||||||
|
server_count: number
|
||||||
|
total_vcpu: number
|
||||||
|
total_mem_bytes: number
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 最新资源总量汇总 */
|
||||||
|
export interface LatestResourceSummaryPayload {
|
||||||
|
physical: ResourceTotalGroup
|
||||||
|
virtual: ResourceTotalGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 按资产聚合项 */
|
||||||
|
export interface AssetMixedSummaryItem {
|
||||||
|
asset_id: number
|
||||||
|
virtual_count: number
|
||||||
|
physical_count: number
|
||||||
|
physical_latest_cpu_usage?: number | null
|
||||||
|
physical_latest_mem_usage?: number | null
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AssetMixedSummaryPayload {
|
||||||
|
total: number
|
||||||
|
data: AssetMixedSummaryItem[]
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 接口1:虚拟机资产概览(支持分类筛选) */
|
||||||
|
export const fetchVirtualOverview = (category?: string) => {
|
||||||
|
return request.get<{ code: number; details?: VirtualOverviewPayload; message?: string }>(
|
||||||
|
'/DC-Control/v1/servers/assets/virtual/overview',
|
||||||
|
{ params: { category } },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 接口2:physical 近24小时 CPU/内存趋势 */
|
||||||
|
export const fetchPhysicalUsageTrend24h = () => {
|
||||||
|
return request.get<{ code: number; details?: PhysicalUsageTrendPayload; message?: string }>(
|
||||||
|
'/DC-Control/v1/servers/physical/usage/trend-24h',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 接口3:physical/virtual 最新资源总量 */
|
||||||
|
export const fetchLatestResourceSummary = () => {
|
||||||
|
return request.get<{ code: number; details?: LatestResourceSummaryPayload; message?: string }>(
|
||||||
|
'/DC-Control/v1/servers/resources/latest/summary',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 接口4:按资产汇总 virtual 数量与 physical 最新指标 */
|
||||||
|
export const fetchAssetMixedSummary = () => {
|
||||||
|
return request.get<{ code: number; details?: AssetMixedSummaryPayload; message?: string }>(
|
||||||
|
'/DC-Control/v1/servers/assets/mixed/summary',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -492,7 +492,7 @@ const resourcePanels = computed<OsResourcePanel[]>(() => {
|
|||||||
sysPanel = { ...defaultResourcePanels[1] }
|
sysPanel = { ...defaultResourcePanels[1] }
|
||||||
}
|
}
|
||||||
|
|
||||||
const cores = d.cpu?.logical_cores_total ?? 0
|
const cores = d.cpu?.physical_threads_total ?? d.cpu?.logical_cores_total ?? 0
|
||||||
const usage = d.cpu?.usage_percent ?? 0
|
const usage = d.cpu?.usage_percent ?? 0
|
||||||
const usedCores = cores > 0 ? (usage / 100) * cores : 0
|
const usedCores = cores > 0 ? (usage / 100) * cores : 0
|
||||||
const freeCores = cores > 0 ? Math.max(0, cores - usedCores) : 0
|
const freeCores = cores > 0 ? Math.max(0, cores - usedCores) : 0
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<div class="stats-info">
|
<div class="stats-info">
|
||||||
<div class="stats-title">虚拟机总数</div>
|
<div class="stats-title">虚拟机总数</div>
|
||||||
<div class="stats-value">{{ stats.total }}</div>
|
<div class="stats-value">{{ stats.total }}</div>
|
||||||
<div class="stats-desc">跨 8 台宿主机</div>
|
<div class="stats-desc">宿主机 {{ stats.assetGroups }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a-card>
|
</a-card>
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
<div class="stats-info">
|
<div class="stats-info">
|
||||||
<div class="stats-title">运行中</div>
|
<div class="stats-title">运行中</div>
|
||||||
<div class="stats-value">{{ stats.running }}</div>
|
<div class="stats-value">{{ stats.running }}</div>
|
||||||
<div class="stats-desc text-success">90.7%</div>
|
<div class="stats-desc text-success">{{ runningRateText }}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a-card>
|
</a-card>
|
||||||
@@ -38,8 +38,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="stats-info">
|
<div class="stats-info">
|
||||||
<div class="stats-title">CPU使用率</div>
|
<div class="stats-title">CPU使用率</div>
|
||||||
<div class="stats-value">{{ stats.cpuUsage }}%</div>
|
<div class="stats-value">{{ statsCpuUsageText }}%</div>
|
||||||
<div class="stats-desc">集群平均</div>
|
<div class="stats-desc">平均</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a-card>
|
</a-card>
|
||||||
@@ -52,8 +52,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="stats-info">
|
<div class="stats-info">
|
||||||
<div class="stats-title">内存使用率</div>
|
<div class="stats-title">内存使用率</div>
|
||||||
<div class="stats-value">{{ stats.memoryUsage }}%</div>
|
<div class="stats-value">{{ statsMemoryUsageText }}%</div>
|
||||||
<div class="stats-desc">集群平均</div>
|
<div class="stats-desc">平均</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a-card>
|
</a-card>
|
||||||
@@ -85,7 +85,7 @@
|
|||||||
<a-col :xs="24" :lg="8">
|
<a-col :xs="24" :lg="8">
|
||||||
<a-card title="CPU资源分配" :bordered="false">
|
<a-card title="CPU资源分配" :bordered="false">
|
||||||
<template #extra>
|
<template #extra>
|
||||||
<span class="text-muted">集群总计 256 vCPU</span>
|
<span class="text-muted">总计 {{ cpuTotalText }} vCPU</span>
|
||||||
</template>
|
</template>
|
||||||
<div class="chart-container">
|
<div class="chart-container">
|
||||||
<Chart :options="cpuChartOptions" height="240px" />
|
<Chart :options="cpuChartOptions" height="240px" />
|
||||||
@@ -93,11 +93,11 @@
|
|||||||
<div class="chart-legend">
|
<div class="chart-legend">
|
||||||
<div class="legend-item">
|
<div class="legend-item">
|
||||||
<span class="legend-dot legend-dot-1"></span>
|
<span class="legend-dot legend-dot-1"></span>
|
||||||
<span>已分配 174 vCPU</span>
|
<span>已使用 {{ cpuVirtualText }} vCPU</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="legend-item">
|
<div class="legend-item">
|
||||||
<span class="legend-dot legend-dot-gray"></span>
|
<span class="legend-dot legend-dot-gray"></span>
|
||||||
<span>可用 82 vCPU</span>
|
<span>总计 {{ cpuPhysicalText }} vCPU</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a-card>
|
</a-card>
|
||||||
@@ -105,7 +105,7 @@
|
|||||||
<a-col :xs="24" :lg="8">
|
<a-col :xs="24" :lg="8">
|
||||||
<a-card title="内存资源分配" :bordered="false">
|
<a-card title="内存资源分配" :bordered="false">
|
||||||
<template #extra>
|
<template #extra>
|
||||||
<span class="text-muted">集群总计 512 GB</span>
|
<span class="text-muted">总计 {{ memoryTotalText }} GB</span>
|
||||||
</template>
|
</template>
|
||||||
<div class="chart-container">
|
<div class="chart-container">
|
||||||
<Chart :options="memoryChartOptions" height="240px" />
|
<Chart :options="memoryChartOptions" height="240px" />
|
||||||
@@ -113,11 +113,11 @@
|
|||||||
<div class="chart-legend">
|
<div class="chart-legend">
|
||||||
<div class="legend-item">
|
<div class="legend-item">
|
||||||
<span class="legend-dot legend-dot-2"></span>
|
<span class="legend-dot legend-dot-2"></span>
|
||||||
<span>已使用 384 GB</span>
|
<span>已使用 {{ memoryVirtualText }} GB</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="legend-item">
|
<div class="legend-item">
|
||||||
<span class="legend-dot legend-dot-gray"></span>
|
<span class="legend-dot legend-dot-gray"></span>
|
||||||
<span>可用 128 GB</span>
|
<span>总计 {{ memoryPhysicalText }} GB</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a-card>
|
</a-card>
|
||||||
@@ -130,7 +130,9 @@
|
|||||||
<a-card class="host-card" :bordered="false">
|
<a-card class="host-card" :bordered="false">
|
||||||
<div class="host-header">
|
<div class="host-header">
|
||||||
<span class="host-name">{{ host.name }}</span>
|
<span class="host-name">{{ host.name }}</span>
|
||||||
<a-tag color="green" size="small">在线</a-tag>
|
<a-tag :color="host.physicalCount > 0 ? 'green' : 'gray'" size="small">
|
||||||
|
{{ host.physicalCount > 0 ? '有物理机' : '仅虚拟机' }}
|
||||||
|
</a-tag>
|
||||||
</div>
|
</div>
|
||||||
<div class="host-metrics">
|
<div class="host-metrics">
|
||||||
<div class="metric-item">
|
<div class="metric-item">
|
||||||
@@ -151,253 +153,58 @@
|
|||||||
<span class="metric-label">虚拟机数</span>
|
<span class="metric-label">虚拟机数</span>
|
||||||
<span class="metric-value-right">{{ host.vmCount }}</span>
|
<span class="metric-value-right">{{ host.vmCount }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="metric-item metric-vm">
|
||||||
|
<span class="metric-label">物理机数</span>
|
||||||
|
<span class="metric-value-right">{{ host.physicalCount }}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a-card>
|
</a-card>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
|
|
||||||
<!-- 虚拟机列表 -->
|
<!-- 虚拟机列表模块按需求下线(暂不渲染) -->
|
||||||
<a-card title="虚拟机列表" :bordered="false">
|
|
||||||
<template #extra>
|
|
||||||
<a-space>
|
|
||||||
<a-select v-model="filterHost" placeholder="全部宿主机" style="width: 130px">
|
|
||||||
<a-option value="">全部宿主机</a-option>
|
|
||||||
<a-option value="ESXi-01">ESXi-01</a-option>
|
|
||||||
<a-option value="ESXi-02">ESXi-02</a-option>
|
|
||||||
<a-option value="ESXi-03">ESXi-03</a-option>
|
|
||||||
<a-option value="ESXi-04">ESXi-04</a-option>
|
|
||||||
</a-select>
|
|
||||||
<a-select v-model="filterStatus" placeholder="全部状态" style="width: 120px">
|
|
||||||
<a-option value="">全部状态</a-option>
|
|
||||||
<a-option value="running">运行中</a-option>
|
|
||||||
<a-option value="stopped">已停止</a-option>
|
|
||||||
<a-option value="warning">异常</a-option>
|
|
||||||
</a-select>
|
|
||||||
</a-space>
|
|
||||||
</template>
|
|
||||||
<a-table
|
|
||||||
:data="filteredVMs"
|
|
||||||
:columns="columns"
|
|
||||||
:loading="loading"
|
|
||||||
:pagination="false"
|
|
||||||
row-key="name"
|
|
||||||
>
|
|
||||||
<!-- 状态列 -->
|
|
||||||
<template #status="{ record }">
|
|
||||||
<a-tag :color="getStatusColor(record.statusValue)" bordered>
|
|
||||||
{{ record.statusText }}
|
|
||||||
</a-tag>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- CPU列 -->
|
|
||||||
<template #cpu="{ record }">
|
|
||||||
<div class="progress-cell">
|
|
||||||
<a-progress
|
|
||||||
:percent="record.cpu / 100"
|
|
||||||
:stroke-width="6"
|
|
||||||
:show-text="false"
|
|
||||||
:status="getProgressStatus(record.cpu)"
|
|
||||||
/>
|
|
||||||
<span class="progress-text">{{ record.cpu }}%</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- 内存列 -->
|
|
||||||
<template #memory="{ record }">
|
|
||||||
<div class="progress-cell">
|
|
||||||
<a-progress
|
|
||||||
:percent="record.memory / 100"
|
|
||||||
:stroke-width="6"
|
|
||||||
:show-text="false"
|
|
||||||
:status="getProgressStatus(record.memory)"
|
|
||||||
/>
|
|
||||||
<span class="progress-text">{{ record.memory }}%</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<!-- 存储列 -->
|
|
||||||
<template #storage="{ record }">
|
|
||||||
<div class="progress-cell">
|
|
||||||
<a-progress
|
|
||||||
:percent="record.storage / 100"
|
|
||||||
:stroke-width="6"
|
|
||||||
:show-text="false"
|
|
||||||
:status="getProgressStatus(record.storage)"
|
|
||||||
/>
|
|
||||||
<span class="progress-text">{{ record.storage }}%</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</a-table>
|
|
||||||
</a-card>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, computed, onMounted } from 'vue'
|
import { ref, computed, onMounted } from 'vue'
|
||||||
|
import { Message } from '@arco-design/web-vue'
|
||||||
import {
|
import {
|
||||||
IconStorage,
|
IconStorage,
|
||||||
IconCheckCircleFill,
|
IconCheckCircleFill,
|
||||||
IconCodeSquare,
|
IconCodeSquare,
|
||||||
IconDriveFile,
|
IconDriveFile,
|
||||||
} from '@arco-design/web-vue/es/icon'
|
} from '@arco-design/web-vue/es/icon'
|
||||||
import Breadcrumb from '@/components/breadcrumb/index.vue'
|
|
||||||
import Chart from '@/components/chart/index.vue'
|
import Chart from '@/components/chart/index.vue'
|
||||||
import type { TableColumnData } from '@arco-design/web-vue/es/table/interface'
|
import {
|
||||||
|
fetchAssetMixedSummary,
|
||||||
|
fetchLatestResourceSummary,
|
||||||
|
fetchPhysicalUsageTrend24h,
|
||||||
|
fetchVirtualOverview,
|
||||||
|
type AssetMixedSummaryItem,
|
||||||
|
} from '@/api/ops/server'
|
||||||
|
|
||||||
// 统计数据
|
// 统计数据
|
||||||
const stats = ref({
|
const stats = ref({
|
||||||
total: 86,
|
total: 0,
|
||||||
running: 78,
|
assetGroups: 0,
|
||||||
cpuUsage: 68,
|
running: 0,
|
||||||
memoryUsage: 75,
|
cpuUsage: 0,
|
||||||
|
memoryUsage: 0,
|
||||||
})
|
})
|
||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const filterHost = ref('')
|
|
||||||
const filterStatus = ref('')
|
|
||||||
|
|
||||||
// 表格列配置
|
|
||||||
const columns: TableColumnData[] = [
|
|
||||||
{
|
|
||||||
title: '虚拟机名称',
|
|
||||||
dataIndex: 'name',
|
|
||||||
width: 130,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '宿主机',
|
|
||||||
dataIndex: 'host',
|
|
||||||
width: 100,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '操作系统',
|
|
||||||
dataIndex: 'os',
|
|
||||||
width: 150,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '状态',
|
|
||||||
dataIndex: 'status',
|
|
||||||
slotName: 'status',
|
|
||||||
width: 100,
|
|
||||||
align: 'center',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'CPU使用率',
|
|
||||||
dataIndex: 'cpu',
|
|
||||||
slotName: 'cpu',
|
|
||||||
width: 150,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '内存使用率',
|
|
||||||
dataIndex: 'memory',
|
|
||||||
slotName: 'memory',
|
|
||||||
width: 150,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '存储使用率',
|
|
||||||
dataIndex: 'storage',
|
|
||||||
slotName: 'storage',
|
|
||||||
width: 150,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '网络流量',
|
|
||||||
dataIndex: 'network',
|
|
||||||
width: 100,
|
|
||||||
align: 'center',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
// 虚拟机数据
|
|
||||||
const vmData = ref([
|
|
||||||
{
|
|
||||||
name: 'VM-Web-01',
|
|
||||||
host: 'ESXi-01',
|
|
||||||
os: 'CentOS 7.9',
|
|
||||||
statusValue: 'running',
|
|
||||||
statusText: '运行中',
|
|
||||||
cpu: 65,
|
|
||||||
memory: 72,
|
|
||||||
storage: 45,
|
|
||||||
network: '1.2 Gbps',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'VM-DB-01',
|
|
||||||
host: 'ESXi-01',
|
|
||||||
os: 'Ubuntu 22.04',
|
|
||||||
statusValue: 'running',
|
|
||||||
statusText: '运行中',
|
|
||||||
cpu: 85,
|
|
||||||
memory: 88,
|
|
||||||
storage: 78,
|
|
||||||
network: '850 Mbps',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'VM-App-01',
|
|
||||||
host: 'ESXi-02',
|
|
||||||
os: 'Windows Server 2022',
|
|
||||||
statusValue: 'running',
|
|
||||||
statusText: '运行中',
|
|
||||||
cpu: 42,
|
|
||||||
memory: 55,
|
|
||||||
storage: 35,
|
|
||||||
network: '450 Mbps',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'VM-Cache-01',
|
|
||||||
host: 'ESXi-02',
|
|
||||||
os: 'CentOS 8',
|
|
||||||
statusValue: 'warning',
|
|
||||||
statusText: '高负载',
|
|
||||||
cpu: 92,
|
|
||||||
memory: 95,
|
|
||||||
storage: 60,
|
|
||||||
network: '2.1 Gbps',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'VM-Dev-01',
|
|
||||||
host: 'ESXi-03',
|
|
||||||
os: 'Ubuntu 20.04',
|
|
||||||
statusValue: 'stopped',
|
|
||||||
statusText: '已停止',
|
|
||||||
cpu: 0,
|
|
||||||
memory: 0,
|
|
||||||
storage: 25,
|
|
||||||
network: '-',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'VM-Test-01',
|
|
||||||
host: 'ESXi-03',
|
|
||||||
os: 'Debian 11',
|
|
||||||
statusValue: 'running',
|
|
||||||
statusText: '运行中',
|
|
||||||
cpu: 28,
|
|
||||||
memory: 35,
|
|
||||||
storage: 42,
|
|
||||||
network: '320 Mbps',
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
// 过滤后的虚拟机列表
|
|
||||||
const filteredVMs = computed(() => {
|
|
||||||
let result = vmData.value
|
|
||||||
|
|
||||||
if (filterHost.value) {
|
|
||||||
result = result.filter((vm) => vm.host === filterHost.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filterStatus.value) {
|
|
||||||
result = result.filter((vm) => vm.statusValue === filterStatus.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
})
|
|
||||||
|
|
||||||
// 宿主机状态
|
// 宿主机状态
|
||||||
const hostStatus = ref([
|
const hostStatus = ref<
|
||||||
{ name: 'ESXi-01', cpu: 65, memory: 78, vmCount: 24 },
|
Array<{
|
||||||
{ name: 'ESXi-02', cpu: 72, memory: 85, vmCount: 22 },
|
name: string
|
||||||
{ name: 'ESXi-03', cpu: 45, memory: 52, vmCount: 18 },
|
cpu: number
|
||||||
{ name: 'ESXi-04', cpu: 58, memory: 68, vmCount: 22 },
|
memory: number
|
||||||
])
|
vmCount: number
|
||||||
|
physicalCount: number
|
||||||
|
}>
|
||||||
|
>([])
|
||||||
|
|
||||||
// 资源使用趋势图表配置
|
// 资源使用趋势图表配置
|
||||||
const performanceChartOptions = ref({
|
const performanceChartOptions = ref({
|
||||||
@@ -413,7 +220,7 @@ const performanceChartOptions = ref({
|
|||||||
xAxis: {
|
xAxis: {
|
||||||
type: 'category',
|
type: 'category',
|
||||||
boundaryGap: false,
|
boundaryGap: false,
|
||||||
data: ['00:00', '04:00', '08:00', '12:00', '16:00', '20:00', '24:00'],
|
data: [] as string[],
|
||||||
},
|
},
|
||||||
yAxis: {
|
yAxis: {
|
||||||
type: 'value',
|
type: 'value',
|
||||||
@@ -425,7 +232,7 @@ const performanceChartOptions = ref({
|
|||||||
name: 'CPU',
|
name: 'CPU',
|
||||||
type: 'line',
|
type: 'line',
|
||||||
smooth: true,
|
smooth: true,
|
||||||
data: [45, 38, 72, 85, 78, 55, 42],
|
data: [] as number[],
|
||||||
lineStyle: {
|
lineStyle: {
|
||||||
width: 2,
|
width: 2,
|
||||||
color: '#165DFF',
|
color: '#165DFF',
|
||||||
@@ -438,7 +245,7 @@ const performanceChartOptions = ref({
|
|||||||
name: '内存',
|
name: '内存',
|
||||||
type: 'line',
|
type: 'line',
|
||||||
smooth: true,
|
smooth: true,
|
||||||
data: [62, 58, 75, 82, 79, 65, 60],
|
data: [] as number[],
|
||||||
lineStyle: {
|
lineStyle: {
|
||||||
width: 2,
|
width: 2,
|
||||||
color: '#F7BA1E',
|
color: '#F7BA1E',
|
||||||
@@ -464,8 +271,8 @@ const cpuChartOptions = ref({
|
|||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
data: [
|
data: [
|
||||||
{ value: 68, name: '已分配', itemStyle: { color: '#165DFF' } },
|
{ value: 0, name: 'virtual', itemStyle: { color: '#165DFF' } },
|
||||||
{ value: 32, name: '可用', itemStyle: { color: '#E5E6EB' } },
|
{ value: 0, name: 'physical', itemStyle: { color: '#E5E6EB' } },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -485,34 +292,106 @@ const memoryChartOptions = ref({
|
|||||||
show: false,
|
show: false,
|
||||||
},
|
},
|
||||||
data: [
|
data: [
|
||||||
{ value: 75, name: '已使用', itemStyle: { color: '#14C9C9' } },
|
{ value: 0, name: 'virtual', itemStyle: { color: '#14C9C9' } },
|
||||||
{ value: 25, name: '可用', itemStyle: { color: '#E5E6EB' } },
|
{ value: 0, name: 'physical', itemStyle: { color: '#E5E6EB' } },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
// 获取状态颜色
|
const cpuVirtual = ref(0)
|
||||||
const getStatusColor = (status: string) => {
|
const cpuPhysical = ref(0)
|
||||||
const colorMap: Record<string, string> = {
|
const memVirtualGb = ref(0)
|
||||||
running: 'green',
|
const memPhysicalGb = ref(0)
|
||||||
stopped: 'gray',
|
|
||||||
warning: 'orange',
|
const roundToTwo = (value: number) => Math.round(value * 100) / 100
|
||||||
}
|
const formatMaxTwoDecimals = (value: number) => {
|
||||||
return colorMap[status] || 'gray'
|
const rounded = roundToTwo(value)
|
||||||
|
return Number.isInteger(rounded) ? String(rounded) : String(rounded)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取进度条状态
|
const runningRateText = computed(() => {
|
||||||
const getProgressStatus = (value: number) => {
|
if (!stats.value.total) return '0%'
|
||||||
if (value >= 90) return 'danger'
|
return `${formatMaxTwoDecimals((stats.value.running / stats.value.total) * 100)}%`
|
||||||
if (value >= 70) return 'warning'
|
})
|
||||||
return 'normal'
|
const statsCpuUsageText = computed(() => formatMaxTwoDecimals(stats.value.cpuUsage))
|
||||||
|
const statsMemoryUsageText = computed(() => formatMaxTwoDecimals(stats.value.memoryUsage))
|
||||||
|
const cpuTotalText = computed(() => formatMaxTwoDecimals(cpuVirtual.value + cpuPhysical.value))
|
||||||
|
const cpuVirtualText = computed(() => formatMaxTwoDecimals(cpuVirtual.value))
|
||||||
|
const cpuPhysicalText = computed(() => formatMaxTwoDecimals(cpuPhysical.value))
|
||||||
|
const memoryTotalText = computed(() => formatMaxTwoDecimals(memVirtualGb.value + memPhysicalGb.value))
|
||||||
|
const memoryVirtualText = computed(() => formatMaxTwoDecimals(memVirtualGb.value))
|
||||||
|
const memoryPhysicalText = computed(() => formatMaxTwoDecimals(memPhysicalGb.value))
|
||||||
|
const toGb = (bytes: number) => bytes / 1024 / 1024 / 1024
|
||||||
|
const safeNum = (v: number | null | undefined) => (typeof v === 'number' ? v : 0)
|
||||||
|
const hourLabel = (v: string) => {
|
||||||
|
const d = new Date(v)
|
||||||
|
if (Number.isNaN(d.getTime())) return v
|
||||||
|
return `${String(d.getHours()).padStart(2, '0')}:00`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取数据
|
// 获取数据
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
// TODO: 从API获取数据
|
loading.value = true
|
||||||
loading.value = false
|
try {
|
||||||
|
const [overviewRes, trendRes, resourceRes, mixedRes] = await Promise.all([
|
||||||
|
fetchVirtualOverview(),
|
||||||
|
fetchPhysicalUsageTrend24h(),
|
||||||
|
fetchLatestResourceSummary(),
|
||||||
|
fetchAssetMixedSummary(),
|
||||||
|
])
|
||||||
|
|
||||||
|
if (overviewRes.code !== 0 || !overviewRes.details) {
|
||||||
|
Message.error(overviewRes.message || '加载虚拟机概览失败')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const overview = overviewRes.details
|
||||||
|
stats.value.total = overview.vm_total || 0
|
||||||
|
stats.value.assetGroups = overview.asset_group_total || 0
|
||||||
|
stats.value.running = overview.online_total || 0
|
||||||
|
stats.value.cpuUsage = roundToTwo(safeNum(overview.latest_cpu_usage_avg))
|
||||||
|
stats.value.memoryUsage = roundToTwo(safeNum(overview.latest_mem_usage_avg))
|
||||||
|
|
||||||
|
if (trendRes.code === 0 && trendRes.details?.points) {
|
||||||
|
const labels = trendRes.details.points.map((p) => hourLabel(p.hour))
|
||||||
|
const cpuSeries = trendRes.details.points.map((p) => roundToTwo(safeNum(p.cpu_used_percent_avg)))
|
||||||
|
const memSeries = trendRes.details.points.map((p) => roundToTwo(safeNum(p.mem_used_percent_avg)))
|
||||||
|
;(performanceChartOptions.value.xAxis as { data: string[] }).data = labels
|
||||||
|
;(performanceChartOptions.value.series[0] as { data: number[] }).data = cpuSeries
|
||||||
|
;(performanceChartOptions.value.series[1] as { data: number[] }).data = memSeries
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resourceRes.code === 0 && resourceRes.details) {
|
||||||
|
cpuVirtual.value = roundToTwo(safeNum(resourceRes.details.virtual?.total_vcpu))
|
||||||
|
cpuPhysical.value = roundToTwo(safeNum(resourceRes.details.physical?.total_vcpu))
|
||||||
|
memVirtualGb.value = roundToTwo(toGb(safeNum(resourceRes.details.virtual?.total_mem_bytes)))
|
||||||
|
memPhysicalGb.value = roundToTwo(toGb(safeNum(resourceRes.details.physical?.total_mem_bytes)))
|
||||||
|
;(cpuChartOptions.value.series[0] as { data: Array<{ value: number; name: string }> }).data = [
|
||||||
|
{ value: cpuVirtual.value, name: 'virtual' },
|
||||||
|
{ value: cpuPhysical.value, name: 'physical' },
|
||||||
|
]
|
||||||
|
;(memoryChartOptions.value.series[0] as { data: Array<{ value: number; name: string }> }).data = [
|
||||||
|
{ value: memVirtualGb.value, name: 'virtual' },
|
||||||
|
{ value: memPhysicalGb.value, name: 'physical' },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mixedRes.code === 0 && mixedRes.details?.data) {
|
||||||
|
hostStatus.value = (mixedRes.details.data as AssetMixedSummaryItem[]).map((item) => ({
|
||||||
|
name: `Asset-${item.asset_id}`,
|
||||||
|
cpu: Number(safeNum(item.physical_latest_cpu_usage).toFixed(2)),
|
||||||
|
memory: Number(safeNum(item.physical_latest_mem_usage).toFixed(2)),
|
||||||
|
vmCount: item.virtual_count || 0,
|
||||||
|
physicalCount: item.physical_count || 0,
|
||||||
|
}))
|
||||||
|
} else {
|
||||||
|
hostStatus.value = []
|
||||||
|
}
|
||||||
|
} catch (e: any) {
|
||||||
|
Message.error(e?.message || '加载虚拟化监控数据失败')
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化
|
// 初始化
|
||||||
|
|||||||
Reference in New Issue
Block a user