修复 空数据时页面组件不显示

This commit is contained in:
zxr
2026-06-29 10:09:28 +08:00
parent cd5e8b5f2d
commit fcaad4b3ae
4 changed files with 122 additions and 64 deletions

View File

@@ -10,7 +10,8 @@ VITE_USE_MOCK=false
# API 基础URL
# VITE_API_BASE_URL=https://ops-api.apinb.com
VITE_API_BASE_URL=http://127.0.0.1
# 开发环境走 Vite 同源代理,避免依赖本机 80/443 nginx
VITE_API_BASE_URL=
# Logs 本地调试地址(仅 logs 模块使用)
VITE_LOGS_API_BASE_URL=http://127.0.0.1:12440

View File

@@ -2,6 +2,11 @@ import { mergeConfig } from 'vite'
// import eslint from 'vite-plugin-eslint'
import baseConfig from './vite.config.base'
const proxyTarget = (port: number) => ({
target: `http://127.0.0.1:${port}`,
changeOrigin: true,
})
export default mergeConfig(
{
mode: 'development',
@@ -11,6 +16,31 @@ export default mergeConfig(
fs: {
strict: true,
},
proxy: {
'/rbac2': proxyTarget(10001),
'/Alert': proxyTarget(12427),
'/alert': proxyTarget(12427),
'/DC-Control': proxyTarget(3031),
'/dc-control': proxyTarget(3031),
'/dc-network': proxyTarget(12429),
'/DC-Hardware': proxyTarget(12450),
'/dc-hardware': proxyTarget(12450),
'/dc-host': proxyTarget(9030),
'/dc-middleware': proxyTarget(12428),
'/dc-database': proxyTarget(12580),
'/Feedback': proxyTarget(12432),
'/feedback': proxyTarget(12432),
'/Assets': proxyTarget(12430),
'/assets': proxyTarget(12430),
'/Logs': proxyTarget(12440),
'/logs': proxyTarget(12440),
'/Kb': proxyTarget(12434),
'/kb': proxyTarget(12434),
'/Mgt': proxyTarget(12436),
'/mgt': proxyTarget(12436),
'/Visual': proxyTarget(12438),
'/visual': proxyTarget(12438),
},
},
plugins: [
// eslint({

View File

@@ -162,6 +162,13 @@ export const fetchServerMetricsSummary = (serverIdentity: string) => {
})
}
/** 立即拉取一次主机指标并返回最新统计卡片 */
export const collectServerMetricsNow = (serverIdentity: string) => {
return request.post<{ code: number; details?: HostMetricsSummary; message?: string }>('/DC-Control/v1/servers/metrics/collect', {
server_identity: serverIdentity,
})
}
/** 近 N 小时网络收/发速率Mbps相邻采样字节差分 */
export const fetchServerNetworkTraffic = (serverIdentity: string, hours = 6) => {
return request.get<{ code: number; details?: HostNetworkTrafficPayload; message?: string }>(

View File

@@ -120,6 +120,12 @@
<icon-down />
</a-button>
<template #content>
<a-doption :disabled="isCollecting(record) || !record.server_identity" @click="handleCollectNow(record)">
<template #icon>
<icon-refresh />
</template>
{{ isCollecting(record) ? '采集中' : '立即采集' }}
</a-doption>
<a-doption @click="handleQuickConfig(record)">
<template #icon>
<icon-settings />
@@ -200,13 +206,7 @@ import ServerFormDialog from './components/ServerFormDialog.vue'
import QuickConfigDialog from './components/QuickConfigDialog.vue'
import HardwareDeviceConfigDialog from './components/HardwareDeviceConfigDialog.vue'
import { columns as columnsConfig } from './config/columns'
import { fetchServerList, deleteServer } from '@/api/ops/server'
import axios from 'axios'
// 创建独立的 axios 实例用于请求外部 agent绕过全局拦截器
const agentAxios = axios.create({
timeout: 5000,
})
import { fetchServerList, deleteServer, fetchServerMetricsSummary, collectServerMetricsNow, type HostMetricsSummary } from '@/api/ops/server'
const router = useRouter()
@@ -217,6 +217,7 @@ const formDialogVisible = ref(false)
const quickConfigVisible = ref(false)
const hardwareConfigVisible = ref(false)
const currentRecord = ref<any>(null)
const collectingMap = reactive<Record<string, boolean>>({})
const formModel = ref({
keyword: '',
collect_on: undefined as boolean | undefined,
@@ -394,6 +395,13 @@ const handleHardwareConfigSuccess = () => {
fetchServers()
}
const getCollectingKey = (record: any) => record.server_identity || String(record.id || '')
const isCollecting = (record: any) => {
const key = getCollectingKey(record)
return !!collectingMap[key]
}
// 编辑服务器
const handleEdit = (record: any) => {
currentRecord.value = record
@@ -465,74 +473,86 @@ const handleDelete = async (record: any) => {
})
}
/** dc-host `GET agent_config`(一般为 `/dc-host/stats`)返回裸 Metrics JSON */
// 获取所有服务器的监控指标
const getAllMetrics = async () => {
try {
// 遍历每个服务器记录
const metricsPromises = tableData.value.map(async (record) => {
// 检查是否有 agent_config 配置
if (record.agent_config) {
try {
// 从 agent_config 中解析 URL
let metricsUrl = record.agent_config
// 验证 URL 是否合法
try {
new URL(metricsUrl)
} catch (urlError) {
console.warn(`服务器 ${record.name} 的 agent_config 不是合法的 URL:`, metricsUrl)
// 设置默认值 0
const resetMetrics = (record: any) => {
record.cpu_info = { value: 0, total: '', used: '' }
record.memory_info = { value: 0, total: '', used: '' }
record.disk_info = { value: 0, total: '', used: '' }
}
const applyMetricsSummary = (record: any, summary?: HostMetricsSummary) => {
if (!summary || !summary.has_data) {
resetMetrics(record)
return
}
// 使用独立的 axios 实例请求外部 agent绕过全局拦截器
const response = await agentAxios.get(metricsUrl)
console.log('获取指标数据:', response.data)
if (response.data) {
// 更新记录的监控数据
record.cpu_info = {
value: Number((response.data.cpu_usage || 0).toFixed(2)),
total: response.data.cpu?.length ? `${response.data.cpu.length}` : '',
value: Number((summary.cpu?.usage_percent || 0).toFixed(2)),
total: summary.cpu?.logical_cores_total ? `${summary.cpu.logical_cores_total}` : '',
used: '',
}
record.memory_info = {
value: Number((response.data.mem_usage?.used_percent || 0).toFixed(2)),
total: response.data.mem_usage?.total ? `${(response.data.mem_usage.total / 1024 / 1024 / 1024).toFixed(1)}GB` : '',
used: response.data.mem_usage?.used ? `${(response.data.mem_usage.used / 1024 / 1024 / 1024).toFixed(1)}GB` : '',
value: Number((summary.memory?.used_percent || 0).toFixed(2)),
total: summary.memory?.total_bytes ? `${(summary.memory.total_bytes / 1024 / 1024 / 1024).toFixed(1)}GB` : '',
used: summary.memory?.used_bytes ? `${(summary.memory.used_bytes / 1024 / 1024 / 1024).toFixed(1)}GB` : '',
}
record.disk_info = {
value: Number((summary.disk_root?.used_percent || 0).toFixed(2)),
total: summary.disk_root?.total_bytes ? `${(summary.disk_root.total_bytes / 1024 / 1024 / 1024).toFixed(0)}GB` : '',
used: summary.disk_root?.used_bytes ? `${(summary.disk_root.used_bytes / 1024 / 1024 / 1024).toFixed(0)}GB` : '',
}
if (summary.timestamp) {
record.last_check_time = summary.timestamp
}
}
record.disk_info = {
value: Number((response.data.disk_usage?.used_percent || 0).toFixed(2)),
total: response.data.disk_usage?.total ? `${(response.data.disk_usage.total / 1024 / 1024 / 1024).toFixed(0)}GB` : '',
used: response.data.disk_usage?.used ? `${(response.data.disk_usage.used / 1024 / 1024 / 1024).toFixed(0)}GB` : '',
}
// 从 dc-control 获取所有服务器最新时序指标
const getAllMetrics = async () => {
try {
const metricsPromises = tableData.value.map(async (record) => {
if (!record.server_identity) {
resetMetrics(record)
return
}
try {
const response = await fetchServerMetricsSummary(record.server_identity)
applyMetricsSummary(record, response.details)
} catch (error) {
console.warn(`获取服务器 ${record.name} 的监控指标失败:`, error)
// 初始化默认值
record.cpu_info = { value: 0, total: '', used: '' }
record.memory_info = { value: 0, total: '', used: '' }
record.disk_info = { value: 0, total: '', used: '' }
}
} else {
// 没有配置 agent设置默认值
record.cpu_info = { value: 0, total: '', used: '' }
record.memory_info = { value: 0, total: '', used: '' }
record.disk_info = { value: 0, total: '', used: '' }
resetMetrics(record)
}
})
// 等待所有请求完成
await Promise.all(metricsPromises)
} catch (error) {
console.error('获取所有服务器监控指标失败:', error)
}
}
const handleCollectNow = async (record: any) => {
if (!record.server_identity) {
Message.warning('缺少服务器唯一标识,无法采集')
return
}
const key = getCollectingKey(record)
if (collectingMap[key]) {
return
}
collectingMap[key] = true
try {
const response = await collectServerMetricsNow(record.server_identity)
if (response.code === 0) {
applyMetricsSummary(record, response.details)
Message.success('采集完成')
} else {
Message.error(response.message || '采集失败')
}
} catch (error) {
console.error('立即采集失败:', error)
Message.error('采集失败')
} finally {
collectingMap[key] = false
}
}
onMounted(() => {
fetchServers()
})