This commit is contained in:
ygx
2026-03-21 16:19:06 +08:00
parent 4708a8bbf7
commit 69e421834b
8 changed files with 501 additions and 364 deletions

View File

@@ -7,7 +7,7 @@
<a-layout> <a-layout>
<a-layout-sider <a-layout-sider
v-if="renderMenu" v-if="renderMenu"
v-show="!hideMenu && !route?.meta?.isNewTab && !route?.meta?.is_full" v-show="!route?.meta?.is_full"
class="layout-sider" class="layout-sider"
:breakpoint="'xl'" :breakpoint="'xl'"
:collapsible="true" :collapsible="true"

View File

@@ -2,44 +2,42 @@ import { DEFAULT_LAYOUT } from '../base'
import { AppRouteRecordRaw } from '../types' import { AppRouteRecordRaw } from '../types'
const REMOTE: AppRouteRecordRaw = { const REMOTE: AppRouteRecordRaw = {
path: '/dc', // path: '/dc',
name: 'DC', // name: 'DC',
component: DEFAULT_LAYOUT, // component: DEFAULT_LAYOUT,
meta: { // meta: {
locale: 'menu.dc', // locale: 'menu.dc',
requiresAuth: true, // requiresAuth: true,
icon: 'icon-desktop', // icon: 'icon-desktop',
order: 99, // order: 99,
hideInMenu: true, // hideInMenu: true,
}, // },
children: [ // children: [
{ // {
path: 'detail', // path: 'detail',
name: 'DCDetail', // name: 'DCDetail',
component: () => import('@/views/ops/pages/dc/detail/index.vue'), // component: () => import('@/views/ops/pages/dc/detail/index.vue'),
meta: { // meta: {
locale: 'menu.dc.detail', // locale: 'menu.dc.detail',
requiresAuth: true, // requiresAuth: true,
roles: ['*'], // roles: ['*'],
hideInMenu: true, // // is_full: true,
is_full: true, // isNewTab: true,
isNewTab: true, // },
}, // },
}, // {
{ // path: 'remote',
path: 'remote', // name: 'DCRemote',
name: 'DCRemote', // component: () => import('@/views/ops/pages/dc/remote/index.vue'),
component: () => import('@/views/ops/pages/dc/remote/index.vue'), // meta: {
meta: { // locale: 'menu.dc.remote',
locale: 'menu.dc.remote', // requiresAuth: true,
requiresAuth: true, // roles: ['*'],
roles: ['*'], // // is_full: true,
hideInMenu: true, // isNewTab: true,
is_full: true, // },
isNewTab: true, // },
}, // ],
},
],
} }
export default REMOTE export default REMOTE

View File

@@ -446,7 +446,7 @@ const handleEdit = (record: any) => {
formDialogVisible.value = true formDialogVisible.value = true
} }
// 详情 - 跳转到独立页面 // 详情 - 在当前窗口打开
const handleDetail = (record: any) => { const handleDetail = (record: any) => {
router.push({ router.push({
path: '/dc/detail', path: '/dc/detail',
@@ -470,9 +470,9 @@ const handleRestart = (record: any) => {
}) })
} }
// 远程控制 - 跳转到独立页面 // 远程控制 - 在新窗口打开
const handleRemoteControl = (record: any) => { const handleRemoteControl = (record: any) => {
router.push({ const url = router.resolve({
path: '/dc/remote', path: '/dc/remote',
query: { query: {
id: record.id, id: record.id,
@@ -480,7 +480,8 @@ const handleRemoteControl = (record: any) => {
ip: record.ip, ip: record.ip,
status: record.status, status: record.status,
}, },
}) }).href
window.open(url, '_blank')
} }
// 表单提交成功 // 表单提交成功

View File

@@ -3,11 +3,11 @@
<!-- 顶部导航栏 --> <!-- 顶部导航栏 -->
<div class="page-header"> <div class="page-header">
<div class="header-left"> <div class="header-left">
<a-button type="text" @click="goBack"> <!-- <a-button type="text" @click="goBack">
<template #icon><icon-left /></template> <template #icon><icon-left /></template>
返回 返回
</a-button> </a-button> -->
<a-divider direction="vertical" /> <!-- <a-divider direction="vertical" /> -->
<span class="server-name">{{ serverName }}</span> <span class="server-name">{{ serverName }}</span>
<a-tag :color="getStatusColor(serverStatus)" size="small">{{ getStatusText(serverStatus) }}</a-tag> <a-tag :color="getStatusColor(serverStatus)" size="small">{{ getStatusText(serverStatus) }}</a-tag>
</div> </div>

View File

@@ -148,6 +148,13 @@
</a-dropdown> </a-dropdown>
</template> </template>
</search-table> </search-table>
<!-- 新增/编辑对话框 -->
<ServerFormDialog
v-model:visible="formDialogVisible"
:record="currentRecord"
@success="handleFormSuccess"
/>
</div> </div>
</template> </template>
@@ -167,6 +174,7 @@ import {
import type { FormItem } from '@/components/search-form/types' import type { FormItem } from '@/components/search-form/types'
import SearchTable from '@/components/search-table/index.vue' import SearchTable from '@/components/search-table/index.vue'
import { searchFormConfig } from './config/search-form' import { searchFormConfig } from './config/search-form'
import ServerFormDialog from '../pc/components/ServerFormDialog.vue'
import { columns as columnsConfig } from './config/columns' import { columns as columnsConfig } from './config/columns'
import { import {
fetchServerList, fetchServerList,
@@ -382,6 +390,8 @@ const mockServerData = [
// 状态管理 // 状态管理
const loading = ref(false) const loading = ref(false)
const tableData = ref<any[]>([]) const tableData = ref<any[]>([])
const formDialogVisible = ref(false)
const currentRecord = ref<any>(null)
const formModel = ref({ const formModel = ref({
keyword: '', keyword: '',
datacenter_id: undefined, datacenter_id: undefined,
@@ -510,8 +520,19 @@ const handleRefresh = () => {
// 新增服务器 // 新增服务器
const handleAdd = () => { const handleAdd = () => {
Message.info('新增服务器功能待实现') currentRecord.value = null
// TODO: 实现新增服务器对话框 formDialogVisible.value = true
}
// 编辑服务器
const handleEdit = (record: any) => {
currentRecord.value = record
formDialogVisible.value = true
}
// 表单提交成功
const handleFormSuccess = () => {
fetchServers()
} }
// 重启服务器 // 重启服务器
@@ -525,7 +546,7 @@ const handleRestart = (record: any) => {
}) })
} }
// 查看详情 - 跳转到独立页面 // 查看详情 - 在当前窗口打开
const handleDetail = (record: any) => { const handleDetail = (record: any) => {
router.push({ router.push({
path: '/dc/detail', path: '/dc/detail',
@@ -538,15 +559,9 @@ const handleDetail = (record: any) => {
}) })
} }
// 编辑服务器 // 远程控制 - 在新窗口打开
const handleEdit = (record: any) => {
Message.info('编辑服务器功能待实现')
// TODO: 实现编辑服务器对话框
}
// 远程控制 - 跳转到独立页面
const handleRemoteControl = (record: any) => { const handleRemoteControl = (record: any) => {
router.push({ const url = router.resolve({
path: '/dc/remote', path: '/dc/remote',
query: { query: {
id: record.id, id: record.id,
@@ -554,7 +569,8 @@ const handleRemoteControl = (record: any) => {
ip: record.ip, ip: record.ip,
status: record.status, status: record.status,
}, },
}) }).href
window.open(url, '_blank')
} }
// 删除服务器 // 删除服务器

View File

@@ -1,38 +1,21 @@
<template> <template>
<div class="network-device-status-panel"> <search-table
<!-- 查询表单 --> :form-model="formModel"
<a-form :model="formModel" layout="inline" class="search-form"> :form-items="formItems"
<a-form-item label="服务标识" field="service_identities"> :data="tableData"
<a-input :columns="columns"
v-model="formModel.service_identities" :loading="loading"
placeholder="多个标识,逗号分隔" :pagination="pagination"
style="width: 250px" title="网络设备状态"
/> @update:form-model="handleFormModelUpdate"
</a-form-item> @search="handleSearch"
@reset="handleReset"
<a-form-item label="指标名称" field="metric_names"> @refresh="handleRefresh"
<a-input
v-model="formModel.metric_names"
placeholder="多个指标名称,逗号分隔(可选)"
style="width: 250px"
/>
</a-form-item>
<a-form-item label="聚合方式" field="aggregation">
<a-select
v-model="formModel.aggregation"
placeholder="请选择聚合方式"
style="width: 120px"
> >
<a-option value="avg">平均值</a-option> <!-- 时间范围选择器插槽 -->
<a-option value="max">最大值</a-option> <template #form-items>
<a-option value="min">最小值</a-option> <a-col :span="8">
<a-option value="sum">求和</a-option> <a-form-item label="时间范围" :label-col-props="{ span: 6 }" :wrapper-col-props="{ span: 18 }">
<a-option value="count">计数</a-option>
</a-select>
</a-form-item>
<a-form-item label="时间范围" field="timeRange">
<a-range-picker <a-range-picker
v-model="formModel.timeRange" v-model="formModel.timeRange"
show-time show-time
@@ -41,63 +24,38 @@
style="width: 380px" style="width: 380px"
/> />
</a-form-item> </a-form-item>
</a-col>
</template>
<a-form-item> <!-- 状态 -->
<a-space> <template #status="{ record }">
<a-button type="primary" :loading="loading" @click="handleSearch">
查询
</a-button>
<a-button @click="handleReset">
重置
</a-button>
</a-space>
</a-form-item>
</a-form>
<!-- 结果表格 -->
<a-table
:data="tableData"
:loading="loading"
:pagination="false"
:bordered="false"
stripe
class="result-table"
>
<template #columns>
<a-table-column title="服务标识" data-index="service_identity" width="200" fixed="left" />
<a-table-column title="服务器标识" data-index="server_identity" width="180" />
<a-table-column title="名称" data-index="name" width="150" />
<a-table-column title="类型" data-index="type" width="120" />
<a-table-column title="主机" data-index="host" width="150" />
<a-table-column title="状态" data-index="status" width="100">
<template #cell="{ record }">
<a-tag :color="getStatusColor(record.status)"> <a-tag :color="getStatusColor(record.status)">
{{ record.status || '-' }} {{ record.status || '-' }}
</a-tag> </a-tag>
</template> </template>
</a-table-column>
<a-table-column title="响应时间" data-index="response_time" width="120"> <!-- 响应时间 -->
<template #cell="{ record }"> <template #response_time="{ record }">
<span>{{ formatResponseTime(record.response_time) }}</span> {{ formatResponseTime(record.response_time) }}
</template> </template>
</a-table-column>
<a-table-column title="运行时间" data-index="uptime" width="120"> <!-- 运行时间 -->
<template #cell="{ record }"> <template #uptime="{ record }">
<span>{{ formatUptime(record.uptime) }}</span> {{ formatUptime(record.uptime) }}
</template> </template>
</a-table-column>
<a-table-column title="启用" data-index="enabled" width="80"> <!-- 启用 -->
<template #cell="{ record }"> <template #enabled="{ record }">
<span>{{ record.enabled ? '是' : '否' }}</span> <span>{{ record.enabled ? '是' : '否' }}</span>
</template> </template>
</a-table-column>
<a-table-column title="最后检查时间" data-index="last_check_time" width="180"> <!-- 最后检查时间 -->
<template #cell="{ record }"> <template #last_check_time="{ record }">
<span>{{ formatTime(record.last_check_time) }}</span> {{ formatTime(record.last_check_time) }}
</template> </template>
</a-table-column>
<a-table-column title="指标数据" width="300"> <!-- 指标数据 -->
<template #cell="{ record }"> <template #metrics="{ record }">
<div v-if="record.metrics && Object.keys(record.metrics).length > 0" class="metrics-cell"> <div v-if="record.metrics && Object.keys(record.metrics).length > 0" class="metrics-cell">
<div v-for="(value, key) in record.metrics" :key="key" class="metric-item"> <div v-for="(value, key) in record.metrics" :key="key" class="metric-item">
<span class="metric-name">{{ key }}:</span> <span class="metric-name">{{ key }}:</span>
@@ -106,22 +64,18 @@
</div> </div>
<span v-else>-</span> <span v-else>-</span>
</template> </template>
</a-table-column> </search-table>
</template>
</a-table>
<!-- 空状态 -->
<a-empty v-if="!loading && tableData.length === 0" description="暂无数据" />
</div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, reactive } from 'vue' import { ref, reactive, computed } from 'vue'
import { Message } from '@arco-design/web-vue' import { Message } from '@arco-design/web-vue'
import type { FormItem } from '@/components/search-form/types'
import SearchTable from '@/components/search-table/index.vue'
import { fetchNetworkDeviceStatus } from '@/api/ops/report' import { fetchNetworkDeviceStatus } from '@/api/ops/report'
// 表单模型 // 表单模型
const formModel = reactive({ const formModel = ref({
service_identities: '', service_identities: '',
metric_names: '', metric_names: '',
aggregation: 'avg', aggregation: 'avg',
@@ -134,6 +88,111 @@ const loading = ref(false)
// 表格数据 // 表格数据
const tableData = ref<any[]>([]) const tableData = ref<any[]>([])
// 分页配置
const pagination = reactive({
current: 1,
pageSize: 20,
total: 0,
})
// 表单项配置
const formItems = computed<FormItem[]>(() => [
{
field: 'service_identities',
label: '服务标识',
type: 'input',
placeholder: '多个标识,逗号分隔',
colProps: { span: 8 },
},
{
field: 'metric_names',
label: '指标名称',
type: 'input',
placeholder: '多个指标名称,逗号分隔(可选)',
colProps: { span: 8 },
},
{
field: 'aggregation',
label: '聚合方式',
type: 'select',
placeholder: '请选择聚合方式',
options: [
{ label: '平均值', value: 'avg' },
{ label: '最大值', value: 'max' },
{ label: '最小值', value: 'min' },
{ label: '求和', value: 'sum' },
{ label: '计数', value: 'count' },
],
colProps: { span: 8 },
},
])
// 表格列配置
const columns = computed(() => [
{
title: '服务标识',
dataIndex: 'service_identity',
width: 200,
fixed: 'left' as const,
},
{
title: '服务器标识',
dataIndex: 'server_identity',
width: 180,
},
{
title: '名称',
dataIndex: 'name',
width: 150,
},
{
title: '类型',
dataIndex: 'type',
width: 120,
},
{
title: '主机',
dataIndex: 'host',
width: 150,
},
{
title: '状态',
dataIndex: 'status',
width: 100,
slotName: 'status',
},
{
title: '响应时间',
dataIndex: 'response_time',
width: 120,
slotName: 'response_time',
},
{
title: '运行时间',
dataIndex: 'uptime',
width: 120,
slotName: 'uptime',
},
{
title: '启用',
dataIndex: 'enabled',
width: 80,
slotName: 'enabled',
},
{
title: '最后检查时间',
dataIndex: 'last_check_time',
width: 180,
slotName: 'last_check_time',
},
{
title: '指标数据',
dataIndex: 'metrics',
width: 300,
slotName: 'metrics',
},
])
// 获取状态颜色 // 获取状态颜色
const getStatusColor = (status: string) => { const getStatusColor = (status: string) => {
if (!status) return 'gray' if (!status) return 'gray'
@@ -193,64 +252,91 @@ const formatMetricValue = (metric: any) => {
// 查询 // 查询
const handleSearch = async () => { const handleSearch = async () => {
// 验证必填项 // 验证必填项
if (!formModel.service_identities) { if (!formModel.value.service_identities) {
Message.warning('请输入服务标识') Message.warning('请输入服务标识')
return return
} }
// 如果填写了指标名称,必须同时填写时间范围 // 如果填写了指标名称,必须同时填写时间范围
if (formModel.metric_names && (!formModel.timeRange || formModel.timeRange.length !== 2)) { if (formModel.value.metric_names && (!formModel.value.timeRange || formModel.value.timeRange.length !== 2)) {
Message.warning('填写指标名称时必须选择时间范围') Message.warning('填写指标名称时必须选择时间范围')
return return
} }
// 如果填写了时间范围,必须同时填写指标名称 // 如果填写了时间范围,必须同时填写指标名称
if (formModel.timeRange && formModel.timeRange.length === 2 && !formModel.metric_names) { if (formModel.value.timeRange && formModel.value.timeRange.length === 2 && !formModel.value.metric_names) {
Message.warning('选择时间范围时必须填写指标名称') Message.warning('选择时间范围时必须填写指标名称')
return return
} }
pagination.current = 1
await fetchTableData()
}
// 获取表格数据
const fetchTableData = async () => {
loading.value = true loading.value = true
try { try {
const params: any = { const params: any = {
service_identities: formModel.service_identities, service_identities: formModel.value.service_identities,
} }
if (formModel.metric_names) { if (formModel.value.metric_names) {
params.metric_names = formModel.metric_names params.metric_names = formModel.value.metric_names
params.aggregation = formModel.aggregation params.aggregation = formModel.value.aggregation
params.start_time = formModel.timeRange[0] params.start_time = formModel.value.timeRange[0]
params.end_time = formModel.timeRange[1] params.end_time = formModel.value.timeRange[1]
} }
const res = await fetchNetworkDeviceStatus(params) const res = await fetchNetworkDeviceStatus(params)
if (res.code === 0) { if (res.code === 0) {
tableData.value = res.data?.data || [] tableData.value = res.data?.data || []
pagination.total = res.data?.total || 0
if (tableData.value.length === 0) { if (tableData.value.length === 0) {
Message.info('未查询到数据') Message.info('未查询到数据')
} }
} else { } else {
Message.error(res.message || '查询失败') Message.error(res.message || '查询失败')
tableData.value = [] tableData.value = []
pagination.total = 0
} }
} catch (error: any) { } catch (error: any) {
console.error('查询失败:', error) console.error('查询失败:', error)
Message.error(error.message || '查询失败') Message.error(error.message || '查询失败')
tableData.value = [] tableData.value = []
pagination.total = 0
} finally { } finally {
loading.value = false loading.value = false
} }
} }
// 处理表单模型更新
const handleFormModelUpdate = (value: any) => {
formModel.value = {
...formModel.value,
...value,
}
}
// 重置 // 重置
const handleReset = () => { const handleReset = () => {
formModel.service_identities = '' formModel.value = {
formModel.metric_names = '' service_identities: '',
formModel.aggregation = 'avg' metric_names: '',
formModel.timeRange = [] aggregation: 'avg',
timeRange: [],
}
pagination.current = 1
tableData.value = [] tableData.value = []
pagination.total = 0
}
// 刷新
const handleRefresh = () => {
fetchTableData()
Message.success('数据已刷新')
} }
</script> </script>
@@ -261,22 +347,7 @@ export default {
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
.network-device-status-panel { .metrics-cell {
.search-form {
margin-bottom: 20px;
padding: 16px;
background-color: var(--color-fill-2);
border-radius: 4px;
:deep(.arco-form-item) {
margin-bottom: 12px;
}
}
.result-table {
background-color: #fff;
.metrics-cell {
max-height: 200px; max-height: 200px;
overflow-y: auto; overflow-y: auto;
@@ -294,7 +365,5 @@ export default {
font-weight: 500; font-weight: 500;
} }
} }
}
}
} }
</style> </style>

View File

@@ -1,38 +1,21 @@
<template> <template>
<div class="server-status-panel"> <search-table
<!-- 查询表单 --> :form-model="formModel"
<a-form :model="formModel" layout="inline" class="search-form"> :form-items="formItems"
<a-form-item label="服务器标识" field="server_identities"> :data="tableData"
<a-input :columns="columns"
v-model="formModel.server_identities" :loading="loading"
placeholder="多个标识,逗号分隔" :pagination="pagination"
style="width: 250px" title="服务器状态"
/> @update:form-model="handleFormModelUpdate"
</a-form-item> @search="handleSearch"
@reset="handleReset"
<a-form-item label="指标名称" field="metric_names"> @refresh="handleRefresh"
<a-input
v-model="formModel.metric_names"
placeholder="多个指标名称,逗号分隔(可选)"
style="width: 250px"
/>
</a-form-item>
<a-form-item label="聚合方式" field="aggregation">
<a-select
v-model="formModel.aggregation"
placeholder="请选择聚合方式"
style="width: 120px"
> >
<a-option value="avg">平均值</a-option> <!-- 时间范围选择器插槽 -->
<a-option value="max">最大值</a-option> <template #form-items>
<a-option value="min">最小值</a-option> <a-col :span="8">
<a-option value="sum">求和</a-option> <a-form-item label="时间范围" :label-col-props="{ span: 6 }" :wrapper-col-props="{ span: 18 }">
<a-option value="count">计数</a-option>
</a-select>
</a-form-item>
<a-form-item label="时间范围" field="timeRange">
<a-range-picker <a-range-picker
v-model="formModel.timeRange" v-model="formModel.timeRange"
show-time show-time
@@ -41,52 +24,28 @@
style="width: 380px" style="width: 380px"
/> />
</a-form-item> </a-form-item>
</a-col>
</template>
<a-form-item> <!-- 状态 -->
<a-space> <template #status="{ record }">
<a-button type="primary" :loading="loading" @click="handleSearch">
查询
</a-button>
<a-button @click="handleReset">
重置
</a-button>
</a-space>
</a-form-item>
</a-form>
<!-- 结果表格 -->
<a-table
:data="tableData"
:loading="loading"
:pagination="false"
:bordered="false"
stripe
class="result-table"
>
<template #columns>
<a-table-column title="服务器标识" data-index="server_identity" width="180" fixed="left" />
<a-table-column title="名称" data-index="name" width="150" />
<a-table-column title="主机" data-index="host" width="150" />
<a-table-column title="IP 地址" data-index="ip_address" width="150" />
<a-table-column title="状态" data-index="status" width="100">
<template #cell="{ record }">
<a-tag :color="getStatusColor(record.status)"> <a-tag :color="getStatusColor(record.status)">
{{ record.status || '-' }} {{ record.status || '-' }}
</a-tag> </a-tag>
</template> </template>
</a-table-column>
<a-table-column title="启用" data-index="enable" width="80"> <!-- 启用 -->
<template #cell="{ record }"> <template #enable="{ record }">
<span>{{ record.enable ? '是' : '否' }}</span> <span>{{ record.enable ? '是' : '否' }}</span>
</template> </template>
</a-table-column>
<a-table-column title="最后检查时间" data-index="last_check_time" width="180"> <!-- 最后检查时间 -->
<template #cell="{ record }"> <template #last_check_time="{ record }">
<span>{{ formatTime(record.last_check_time) }}</span> {{ formatTime(record.last_check_time) }}
</template> </template>
</a-table-column>
<a-table-column title="指标数据" width="300"> <!-- 指标数据 -->
<template #cell="{ record }"> <template #metrics="{ record }">
<div v-if="record.metrics && Object.keys(record.metrics).length > 0" class="metrics-cell"> <div v-if="record.metrics && Object.keys(record.metrics).length > 0" class="metrics-cell">
<div v-for="(value, key) in record.metrics" :key="key" class="metric-item"> <div v-for="(value, key) in record.metrics" :key="key" class="metric-item">
<span class="metric-name">{{ key }}:</span> <span class="metric-name">{{ key }}:</span>
@@ -95,22 +54,18 @@
</div> </div>
<span v-else>-</span> <span v-else>-</span>
</template> </template>
</a-table-column> </search-table>
</template>
</a-table>
<!-- 空状态 -->
<a-empty v-if="!loading && tableData.length === 0" description="暂无数据" />
</div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, reactive } from 'vue' import { ref, reactive, computed } from 'vue'
import { Message } from '@arco-design/web-vue' import { Message } from '@arco-design/web-vue'
import type { FormItem } from '@/components/search-form/types'
import SearchTable from '@/components/search-table/index.vue'
import { fetchServerStatus } from '@/api/ops/report' import { fetchServerStatus } from '@/api/ops/report'
// 表单模型 // 表单模型
const formModel = reactive({ const formModel = ref({
server_identities: '', server_identities: '',
metric_names: '', metric_names: '',
aggregation: 'avg', aggregation: 'avg',
@@ -123,6 +78,94 @@ const loading = ref(false)
// 表格数据 // 表格数据
const tableData = ref<any[]>([]) const tableData = ref<any[]>([])
// 分页配置
const pagination = reactive({
current: 1,
pageSize: 20,
total: 0,
})
// 表单项配置
const formItems = computed<FormItem[]>(() => [
{
field: 'server_identities',
label: '服务器标识',
type: 'input',
placeholder: '多个标识,逗号分隔',
colProps: { span: 8 },
},
{
field: 'metric_names',
label: '指标名称',
type: 'input',
placeholder: '多个指标名称,逗号分隔(可选)',
colProps: { span: 8 },
},
{
field: 'aggregation',
label: '聚合方式',
type: 'select',
placeholder: '请选择聚合方式',
options: [
{ label: '平均值', value: 'avg' },
{ label: '最大值', value: 'max' },
{ label: '最小值', value: 'min' },
{ label: '求和', value: 'sum' },
{ label: '计数', value: 'count' },
],
colProps: { span: 8 },
},
])
// 表格列配置
const columns = computed(() => [
{
title: '服务器标识',
dataIndex: 'server_identity',
width: 180,
fixed: 'left' as const,
},
{
title: '名称',
dataIndex: 'name',
width: 150,
},
{
title: '主机',
dataIndex: 'host',
width: 150,
},
{
title: 'IP 地址',
dataIndex: 'ip_address',
width: 150,
},
{
title: '状态',
dataIndex: 'status',
width: 100,
slotName: 'status',
},
{
title: '启用',
dataIndex: 'enable',
width: 80,
slotName: 'enable',
},
{
title: '最后检查时间',
dataIndex: 'last_check_time',
width: 180,
slotName: 'last_check_time',
},
{
title: '指标数据',
dataIndex: 'metrics',
width: 300,
slotName: 'metrics',
},
])
// 获取状态颜色 // 获取状态颜色
const getStatusColor = (status: string) => { const getStatusColor = (status: string) => {
if (!status) return 'gray' if (!status) return 'gray'
@@ -159,64 +202,91 @@ const formatMetricValue = (metric: any) => {
// 查询 // 查询
const handleSearch = async () => { const handleSearch = async () => {
// 验证必填项 // 验证必填项
if (!formModel.server_identities) { if (!formModel.value.server_identities) {
Message.warning('请输入服务器标识') Message.warning('请输入服务器标识')
return return
} }
// 如果填写了指标名称,必须同时填写时间范围 // 如果填写了指标名称,必须同时填写时间范围
if (formModel.metric_names && (!formModel.timeRange || formModel.timeRange.length !== 2)) { if (formModel.value.metric_names && (!formModel.value.timeRange || formModel.value.timeRange.length !== 2)) {
Message.warning('填写指标名称时必须选择时间范围') Message.warning('填写指标名称时必须选择时间范围')
return return
} }
// 如果填写了时间范围,必须同时填写指标名称 // 如果填写了时间范围,必须同时填写指标名称
if (formModel.timeRange && formModel.timeRange.length === 2 && !formModel.metric_names) { if (formModel.value.timeRange && formModel.value.timeRange.length === 2 && !formModel.value.metric_names) {
Message.warning('选择时间范围时必须填写指标名称') Message.warning('选择时间范围时必须填写指标名称')
return return
} }
pagination.current = 1
await fetchTableData()
}
// 获取表格数据
const fetchTableData = async () => {
loading.value = true loading.value = true
try { try {
const params: any = { const params: any = {
server_identities: formModel.server_identities, server_identities: formModel.value.server_identities,
} }
if (formModel.metric_names) { if (formModel.value.metric_names) {
params.metric_names = formModel.metric_names params.metric_names = formModel.value.metric_names
params.aggregation = formModel.aggregation params.aggregation = formModel.value.aggregation
params.start_time = formModel.timeRange[0] params.start_time = formModel.value.timeRange[0]
params.end_time = formModel.timeRange[1] params.end_time = formModel.value.timeRange[1]
} }
const res = await fetchServerStatus(params) const res = await fetchServerStatus(params)
if (res.code === 0) { if (res.code === 0) {
tableData.value = res.data?.data || [] tableData.value = res.data?.data || []
pagination.total = res.data?.total || 0
if (tableData.value.length === 0) { if (tableData.value.length === 0) {
Message.info('未查询到数据') Message.info('未查询到数据')
} }
} else { } else {
Message.error(res.message || '查询失败') Message.error(res.message || '查询失败')
tableData.value = [] tableData.value = []
pagination.total = 0
} }
} catch (error: any) { } catch (error: any) {
console.error('查询失败:', error) console.error('查询失败:', error)
Message.error(error.message || '查询失败') Message.error(error.message || '查询失败')
tableData.value = [] tableData.value = []
pagination.total = 0
} finally { } finally {
loading.value = false loading.value = false
} }
} }
// 处理表单模型更新
const handleFormModelUpdate = (value: any) => {
formModel.value = {
...formModel.value,
...value,
}
}
// 重置 // 重置
const handleReset = () => { const handleReset = () => {
formModel.server_identities = '' formModel.value = {
formModel.metric_names = '' server_identities: '',
formModel.aggregation = 'avg' metric_names: '',
formModel.timeRange = [] aggregation: 'avg',
timeRange: [],
}
pagination.current = 1
tableData.value = [] tableData.value = []
pagination.total = 0
}
// 刷新
const handleRefresh = () => {
fetchTableData()
Message.success('数据已刷新')
} }
</script> </script>
@@ -227,22 +297,7 @@ export default {
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
.server-status-panel { .metrics-cell {
.search-form {
margin-bottom: 20px;
padding: 16px;
background-color: var(--color-fill-2);
border-radius: 4px;
:deep(.arco-form-item) {
margin-bottom: 12px;
}
}
.result-table {
background-color: #fff;
.metrics-cell {
max-height: 200px; max-height: 200px;
overflow-y: auto; overflow-y: auto;
@@ -260,7 +315,5 @@ export default {
font-weight: 500; font-weight: 500;
} }
} }
}
}
} }
</style> </style>

View File

@@ -47,7 +47,7 @@ import MetricsSummaryPanel from './components/MetricsSummaryPanel.vue'
import TrafficSummaryPanel from './components/TrafficSummaryPanel.vue' import TrafficSummaryPanel from './components/TrafficSummaryPanel.vue'
import TrafficTrendPanel from './components/TrafficTrendPanel.vue' import TrafficTrendPanel from './components/TrafficTrendPanel.vue'
import ServerStatusPanel from './components/ServerStatusPanel.vue' import ServerStatusPanel from './components/ServerStatusPanel.vue'
import NetworkStatusPanel from './components/NetworkStatusPanel.vue' import NetworkStatusPanel from './components/NetworkDeviceStatusPanel.vue'
// 当前激活的标签页 // 当前激活的标签页
const activeTab = ref('metrics-topn') const activeTab = ref('metrics-topn')