diff --git a/src/api/ops/network-device.ts b/src/api/ops/network-device.ts new file mode 100644 index 0000000..0f96090 --- /dev/null +++ b/src/api/ops/network-device.ts @@ -0,0 +1,94 @@ +import { request } from '@/api/request' + +export type NetworkDeviceProtocol = 'snmp' | 'ssh' | 'telnet' +export type NetworkDeviceSnmpVersion = 'v1' | 'v2c' | 'v3' +export type NetworkDeviceStatus = 'online' | 'offline' | 'error' | 'unknown' + +export interface NetworkDeviceService { + id: number + created_at: string + updated_at: string + deleted_at: string | null + service_identity: string + server_identity: string + name: string + category: string + type: string + host: string + port: number + protocol: NetworkDeviceProtocol + community: string + snmp_version: NetworkDeviceSnmpVersion + username: string + vendor: string + model: string + location: string + description: string + enabled: boolean + interval: number + extra: string + tags: string + status: NetworkDeviceStatus + status_code: number + status_message: string + response_time: number + last_check_time: string + last_online_time: string | null + last_offline_time: string | null + continuous_errors: number + uptime: number +} + +export interface NetworkDeviceListResponse { + total: number + page: number + page_size: number + data: NetworkDeviceService[] +} + +export interface NetworkDeviceQueryParams { + page?: number + size?: number + keyword?: string + enabled?: boolean +} + +export interface CreateNetworkDeviceParams { + service_identity?: string + server_identity?: string + name: string + category: string + type?: string + host: string + port?: number + protocol?: NetworkDeviceProtocol + community?: string + snmp_version?: NetworkDeviceSnmpVersion + username?: string + password?: string + vendor?: string + model?: string + location?: string + description?: string + enabled?: boolean + interval?: number + extra?: string + tags?: string +} + +export type UpdateNetworkDeviceParams = Partial + +export const fetchNetworkDeviceList = (params: NetworkDeviceQueryParams) => + request.get('/DC-Control/v1/network-device-services', { params }) + +export const fetchNetworkDeviceDetail = (id: number) => + request.get(`/DC-Control/v1/network-device-services/${id}`) + +export const createNetworkDevice = (data: CreateNetworkDeviceParams) => + request.post<{ message: string; id: number }>('/DC-Control/v1/network-device-services', data) + +export const updateNetworkDevice = (id: number, data: UpdateNetworkDeviceParams) => + request.put<{ message: string }>(`/DC-Control/v1/network-device-services/${id}`, data) + +export const deleteNetworkDevice = (id: number) => + request.delete<{ message: string }>(`/DC-Control/v1/network-device-services/${id}`) diff --git a/src/api/ops/network-screen.ts b/src/api/ops/network-screen.ts new file mode 100644 index 0000000..847acd6 --- /dev/null +++ b/src/api/ops/network-screen.ts @@ -0,0 +1,29 @@ +import { request } from '@/api/request' + +export const fetchNetworkScreenOverview = () => + request.get('/DC-Control/v1/network-screen/overview') + +export const fetchNetworkScreenHealthMatrix = (params?: { window?: string }) => + request.get('/DC-Control/v1/network-screen/health-matrix', { params }) + +export const fetchNetworkScreenFreshnessDistribution = () => + request.get('/DC-Control/v1/network-screen/freshness-distribution') + +export const fetchNetworkScreenProtocolDistribution = () => + request.get('/DC-Control/v1/network-screen/protocol-distribution') + +export const fetchNetworkScreenResourceOverview = (params?: { window?: string }) => + request.get('/DC-Control/v1/network-screen/resource-overview', { params }) + +export const fetchNetworkScreenAlertsStream = (params?: { limit?: number; window?: string }) => + request.get('/DC-Control/v1/network-screen/alerts-stream', { params }) + +export const fetchNetworkScreenDevices = (params?: { + page?: number + size?: number + keyword?: string + status?: string + enabled?: boolean + protocol?: string + vendor?: string +}) => request.get('/DC-Control/v1/network-screen/devices', { params }) diff --git a/src/router/local-menu-flat.ts b/src/router/local-menu-flat.ts index 0cae7d5..a42f2a9 100644 --- a/src/router/local-menu-flat.ts +++ b/src/router/local-menu-flat.ts @@ -154,8 +154,8 @@ export const localMenuFlatItems: MenuItem[] = [ { id: 22, identity: '019b591d-0159-7e46-a5e0-fb69c6b62a25', - title: '网络设备采集管理', - title_en: 'Network Device Collection Management', + title: '网络设备数据采集管理', + title_en: 'Network Device Data Collection Management', code: 'ops:集群采集控制中心:网络设备采集管理', description: '集群采集控制中心 - 网络设备采集管理', app_id: 2, diff --git a/src/router/local-menu-items.ts b/src/router/local-menu-items.ts index aea9399..dd7453e 100644 --- a/src/router/local-menu-items.ts +++ b/src/router/local-menu-items.ts @@ -164,8 +164,8 @@ export const localMenuItems: MenuItem[] = [ { id: 22, identity: '019b591d-0159-7e46-a5e0-fb69c6b62a25', - title: '网络设备采集管理', - title_en: 'Network Device Collection Management', + title: '网络设备数据采集管理', + title_en: 'Network Device Data Collection Management', code: 'ops:集群采集控制中心:网络设备采集管理', description: '集群采集控制中心 - 网络设备采集管理', app_id: 2, diff --git a/src/views/ops/pages/alert/notice/components/ChannelDetailDialog.vue b/src/views/ops/pages/alert/notice/components/ChannelDetailDialog.vue index 4981598..12554c7 100644 --- a/src/views/ops/pages/alert/notice/components/ChannelDetailDialog.vue +++ b/src/views/ops/pages/alert/notice/components/ChannelDetailDialog.vue @@ -177,7 +177,10 @@ const getQuietHoursDisplay = () => { const formatTime = (time?: string) => { if (!time) return '' try { + if (time.startsWith('0001-01-01')) return '' const date = new Date(time) + if (Number.isNaN(date.getTime())) return '' + if (date.getFullYear() <= 1970) return '' return date.toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', diff --git a/src/views/ops/pages/dc/database/components/CollectDialog.vue b/src/views/ops/pages/dc/database/components/CollectDialog.vue index fa1d6ed..6e9ed20 100644 --- a/src/views/ops/pages/dc/database/components/CollectDialog.vue +++ b/src/views/ops/pages/dc/database/components/CollectDialog.vue @@ -154,7 +154,10 @@ const rules = { // 格式化时间 const formatTime = (time: string) => { if (!time) return '-' + if (time.startsWith('0001-01-01')) return '-' const date = new Date(time) + if (Number.isNaN(date.getTime())) return '-' + if (date.getFullYear() <= 1970) return '-' const year = date.getFullYear() const month = String(date.getMonth() + 1).padStart(2, '0') const day = String(date.getDate()).padStart(2, '0') diff --git a/src/views/ops/pages/dc/database/components/DetailDialog.vue b/src/views/ops/pages/dc/database/components/DetailDialog.vue index 9f3876d..62c19e0 100644 --- a/src/views/ops/pages/dc/database/components/DetailDialog.vue +++ b/src/views/ops/pages/dc/database/components/DetailDialog.vue @@ -234,7 +234,10 @@ const formatUptime = (uptime?: number) => { // 格式化时间 const formatTime = (time?: string | null) => { if (!time) return '-' + if (time.startsWith('0001-01-01')) return '-' const date = new Date(time) + if (Number.isNaN(date.getTime())) return '-' + if (date.getFullYear() <= 1970) return '-' const year = date.getFullYear() const month = String(date.getMonth() + 1).padStart(2, '0') const day = String(date.getDate()).padStart(2, '0') diff --git a/src/views/ops/pages/dc/database/index.vue b/src/views/ops/pages/dc/database/index.vue index 07d35e6..71008b9 100644 --- a/src/views/ops/pages/dc/database/index.vue +++ b/src/views/ops/pages/dc/database/index.vue @@ -253,7 +253,10 @@ const formatUptime = (uptime: number) => { // 格式化时间 const formatTime = (time: string) => { if (!time) return '-' + if (time.startsWith('0001-01-01')) return '-' const date = new Date(time) + if (Number.isNaN(date.getTime())) return '-' + if (date.getFullYear() <= 1970) return '-' const year = date.getFullYear() const month = String(date.getMonth() + 1).padStart(2, '0') const day = String(date.getDate()).padStart(2, '0') diff --git a/src/views/ops/pages/dc/device-collect/components/Detail.vue b/src/views/ops/pages/dc/device-collect/components/Detail.vue index 235e883..111a10c 100644 --- a/src/views/ops/pages/dc/device-collect/components/Detail.vue +++ b/src/views/ops/pages/dc/device-collect/components/Detail.vue @@ -124,7 +124,10 @@ const handleViewMetrics = async () => { const formatTime = (time?: string) => { if (!time) return '-' + if (time.startsWith('0001-01-01')) return '-' const date = new Date(time) + if (Number.isNaN(date.getTime())) return '-' + if (date.getFullYear() <= 1970) return '-' const year = date.getFullYear() const month = String(date.getMonth() + 1).padStart(2, '0') const day = String(date.getDate()).padStart(2, '0') diff --git a/src/views/ops/pages/dc/middleware/components/Detail.vue b/src/views/ops/pages/dc/middleware/components/Detail.vue index b7b8b01..257e765 100644 --- a/src/views/ops/pages/dc/middleware/components/Detail.vue +++ b/src/views/ops/pages/dc/middleware/components/Detail.vue @@ -530,7 +530,11 @@ const formatUptime = (uptime?: number) => { // 格式化时间 const formatTime = (time?: string | null) => { if (!time) return '-' - return new Date(time).toLocaleString('zh-CN') + if (time.startsWith('0001-01-01')) return '-' + const date = new Date(time) + if (Number.isNaN(date.getTime())) return '-' + if (date.getFullYear() <= 1970) return '-' + return date.toLocaleString('zh-CN') } // 格式化额外配置 diff --git a/src/views/ops/pages/dc/middleware/index.vue b/src/views/ops/pages/dc/middleware/index.vue index 9ca6cec..795d3a3 100644 --- a/src/views/ops/pages/dc/middleware/index.vue +++ b/src/views/ops/pages/dc/middleware/index.vue @@ -241,7 +241,10 @@ const formatUptime = (uptime?: number) => { // 格式化时间 const formatTime = (time?: string) => { if (!time) return '-' + if (time.startsWith('0001-01-01')) return '-' const date = new Date(time) + if (Number.isNaN(date.getTime())) return '-' + if (date.getFullYear() <= 1970) return '-' const year = date.getFullYear() const month = String(date.getMonth() + 1).padStart(2, '0') const day = String(date.getDate()).padStart(2, '0') diff --git a/src/views/ops/pages/dc/network/components/Detail.vue b/src/views/ops/pages/dc/network/components/Detail.vue index a40f3e8..8422055 100644 --- a/src/views/ops/pages/dc/network/components/Detail.vue +++ b/src/views/ops/pages/dc/network/components/Detail.vue @@ -1,705 +1,120 @@ diff --git a/src/views/ops/pages/dc/network/components/FormDialog.vue b/src/views/ops/pages/dc/network/components/FormDialog.vue index 40ec95e..87b49da 100644 --- a/src/views/ops/pages/dc/network/components/FormDialog.vue +++ b/src/views/ops/pages/dc/network/components/FormDialog.vue @@ -1,182 +1,254 @@ + + diff --git a/src/views/ops/pages/dc/network/config/columns.ts b/src/views/ops/pages/dc/network/config/columns.ts index 91ab230..632ce56 100644 --- a/src/views/ops/pages/dc/network/config/columns.ts +++ b/src/views/ops/pages/dc/network/config/columns.ts @@ -1,60 +1,48 @@ export const columns = [ - { - dataIndex: 'id', - title: 'ID', - width: 80, - slotName: 'id', - }, - { - dataIndex: 'unique_id', - title: 'OID', - width: 150, - }, { dataIndex: 'name', - title: '名称', - width: 150, + title: '设备名称', + width: 180, }, { - dataIndex: 'type', - title: '类型', + dataIndex: 'category', + title: '设备类型', + width: 120, + slotName: 'category', + }, + { + dataIndex: 'vendor', + title: '厂商', width: 120, }, { - dataIndex: 'manu', - title: '厂商', - width: 150, - }, - { - dataIndex: 'model_num', + dataIndex: 'model', title: '型号', width: 150, }, { - dataIndex: 'location', - title: '位置信息', - width: 150, - }, - { - dataIndex: 'tags', - title: '标签', - width: 120, - }, - { - dataIndex: 'ip', - title: 'IP地址', - width: 150, + dataIndex: 'host', + title: 'IP:端口', + width: 170, + slotName: 'host_port', }, { dataIndex: 'protocol', - title: '通讯协议', - width: 100, + title: '协议', + width: 130, + slotName: 'protocol', }, { - dataIndex: 'indicator', - title: '网络设备指标', - width: 150, - slotName: 'indicator', + dataIndex: 'interval', + title: '采集间隔', + width: 110, + slotName: 'interval', + }, + { + dataIndex: 'enabled', + title: '启用状态', + width: 100, + slotName: 'enabled', }, { dataIndex: 'status', @@ -62,10 +50,28 @@ export const columns = [ width: 100, slotName: 'status', }, + { + dataIndex: 'response_time', + title: '响应时间', + width: 110, + slotName: 'response_time', + }, + { + dataIndex: 'last_check_time', + title: '最近检查', + width: 180, + slotName: 'last_check_time', + }, + { + dataIndex: 'tags', + title: '标签', + width: 180, + slotName: 'tags', + }, { dataIndex: 'actions', title: '操作', - width: 180, + width: 200, fixed: 'right' as const, slotName: 'actions', }, diff --git a/src/views/ops/pages/dc/network/config/search-form.ts b/src/views/ops/pages/dc/network/config/search-form.ts index af2affe..f3b76f9 100644 --- a/src/views/ops/pages/dc/network/config/search-form.ts +++ b/src/views/ops/pages/dc/network/config/search-form.ts @@ -5,35 +5,42 @@ export const searchFormConfig: FormItem[] = [ field: 'keyword', label: '关键词', type: 'input', - placeholder: '请输入服务器名称、编码或IP', + placeholder: '请输入名称/IP/厂商/型号', span: 6, }, { - field: 'datacenter_id', - label: '数据中心', + field: 'enabled', + label: '启用状态', type: 'select', - placeholder: '请选择数据中心', - options: [], // 需要动态加载 - span: 6, - }, - { - field: 'rack_id', - label: '机柜', - type: 'select', - placeholder: '请选择机柜', - options: [], // 需要动态加载 + placeholder: '全部', + options: [ + { label: '已启用', value: true }, + { label: '已停用', value: false }, + ], span: 6, }, { field: 'status', - label: '状态', + label: '设备状态', type: 'select', - placeholder: '请选择状态', + placeholder: '全部', options: [ { label: '在线', value: 'online' }, { label: '离线', value: 'offline' }, - { label: '维护中', value: 'maintenance' }, - { label: '已退役', value: 'retired' }, + { label: '异常', value: 'error' }, + { label: '未知', value: 'unknown' }, + ], + span: 6, + }, + { + field: 'protocol', + label: '采集协议', + type: 'select', + placeholder: '全部', + options: [ + { label: 'SNMP', value: 'snmp' }, + { label: 'SSH', value: 'ssh' }, + { label: 'Telnet', value: 'telnet' }, ], span: 6, }, diff --git a/src/views/ops/pages/dc/network/index.vue b/src/views/ops/pages/dc/network/index.vue index 0a27d3b..a0ccd34 100644 --- a/src/views/ops/pages/dc/network/index.vue +++ b/src/views/ops/pages/dc/network/index.vue @@ -7,7 +7,7 @@ :columns="columns" :loading="loading" :pagination="pagination" - title="网络设备管理" + title="网络设备数据采集管理" search-button-text="查询" reset-button-text="重置" @update:form-model="handleFormModelUpdate" @@ -16,298 +16,58 @@ @page-change="handlePageChange" @refresh="handleRefresh" > - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + - - - - - + + + + @@ -527,38 +269,4 @@ export default { .container { margin-top: 20px; } - -.resource-display { - display: flex; - flex-direction: column; - gap: 4px; - padding: 4px 0; - - .resource-info { - display: flex; - align-items: center; - justify-content: space-between; - > div { - display: inline-block; - } - .resource-value { - font-size: 12px; - font-weight: 500; - color: rgb(var(--text-1)); - } - } - - :deep(.arco-progress) { - margin: 0; - - .arco-progress-bar-bg { - border-radius: 2px; - } - - .arco-progress-bar { - border-radius: 2px; - transition: all 0.3s ease; - } - } -} diff --git a/src/views/ops/pages/dc/security/components/Detail.vue b/src/views/ops/pages/dc/security/components/Detail.vue index c793c74..a59d56d 100644 --- a/src/views/ops/pages/dc/security/components/Detail.vue +++ b/src/views/ops/pages/dc/security/components/Detail.vue @@ -160,7 +160,10 @@ const handleViewMetrics = async () => { const formatTime = (time?: string) => { if (!time) return '-' + if (time.startsWith('0001-01-01')) return '-' const date = new Date(time) + if (Number.isNaN(date.getTime())) return '-' + if (date.getFullYear() <= 1970) return '-' const year = date.getFullYear() const month = String(date.getMonth() + 1).padStart(2, '0') const day = String(date.getDate()).padStart(2, '0') diff --git a/src/views/ops/pages/dc/security/index.vue b/src/views/ops/pages/dc/security/index.vue index 8170748..2ae4f3e 100644 --- a/src/views/ops/pages/dc/security/index.vue +++ b/src/views/ops/pages/dc/security/index.vue @@ -310,7 +310,10 @@ const loadServerOptions = async () => { const formatTime = (time?: string) => { if (!time) return '-' + if (time.startsWith('0001-01-01')) return '-' const date = new Date(time) + if (Number.isNaN(date.getTime())) return '-' + if (date.getFullYear() <= 1970) return '-' const year = date.getFullYear() const month = String(date.getMonth() + 1).padStart(2, '0') const day = String(date.getDate()).padStart(2, '0') diff --git a/src/views/ops/pages/dc/server/components/ServerDetail.vue b/src/views/ops/pages/dc/server/components/ServerDetail.vue index a8433e7..77f7c9a 100644 --- a/src/views/ops/pages/dc/server/components/ServerDetail.vue +++ b/src/views/ops/pages/dc/server/components/ServerDetail.vue @@ -578,6 +578,8 @@ const formatTime = (time?: string | Date) => { if (!time) return '-' try { const date = new Date(time) + if (Number.isNaN(date.getTime())) return '-' + if (date.getFullYear() <= 1970) return '-' return date.toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', diff --git a/src/views/ops/pages/dc/url-harvest/components/Detail.vue b/src/views/ops/pages/dc/url-harvest/components/Detail.vue index 8738448..acfe19f 100644 --- a/src/views/ops/pages/dc/url-harvest/components/Detail.vue +++ b/src/views/ops/pages/dc/url-harvest/components/Detail.vue @@ -100,7 +100,7 @@ const getStatusText = (status?: string) => { const formatTime = (time?: string | null) => { if (!time || time.startsWith('0001-01-01')) return '-' const date = new Date(time) - if (Number.isNaN(date.getTime()) || date.getFullYear() <= 1) return '-' + if (Number.isNaN(date.getTime()) || date.getFullYear() <= 1970) return '-' const year = date.getFullYear() const month = String(date.getMonth() + 1).padStart(2, '0') const day = String(date.getDate()).padStart(2, '0') diff --git a/src/views/ops/pages/dc/url-harvest/index.vue b/src/views/ops/pages/dc/url-harvest/index.vue index b744673..24a6bac 100644 --- a/src/views/ops/pages/dc/url-harvest/index.vue +++ b/src/views/ops/pages/dc/url-harvest/index.vue @@ -159,7 +159,7 @@ const getStatusText = (status?: string) => { const formatTime = (time?: string | null) => { if (!time || time.startsWith('0001-01-01')) return '-' const date = new Date(time) - if (Number.isNaN(date.getTime()) || date.getFullYear() <= 1) return '-' + if (Number.isNaN(date.getTime()) || date.getFullYear() <= 1970) return '-' const year = date.getFullYear() const month = String(date.getMonth() + 1).padStart(2, '0') const day = String(date.getDate()).padStart(2, '0') diff --git a/src/views/ops/pages/monitor/network/index.vue b/src/views/ops/pages/monitor/network/index.vue index 60d6338..e598116 100644 --- a/src/views/ops/pages/monitor/network/index.vue +++ b/src/views/ops/pages/monitor/network/index.vue @@ -1,218 +1,108 @@ \ No newline at end of file diff --git a/src/views/ops/pages/monitor/security/index.vue b/src/views/ops/pages/monitor/security/index.vue index db8c491..53395aa 100644 --- a/src/views/ops/pages/monitor/security/index.vue +++ b/src/views/ops/pages/monitor/security/index.vue @@ -353,7 +353,11 @@ const formatThroughput = (val: number | null) => { const formatTime = (time: string) => { if (!time) return '-' - return new Date(time).toLocaleString('zh-CN') + if (time.startsWith('0001-01-01')) return '-' + const date = new Date(time) + if (Number.isNaN(date.getTime())) return '-' + if (date.getFullYear() <= 1970) return '-' + return date.toLocaleString('zh-CN') } const getStatusColor = (status: string) => { diff --git a/src/views/ops/pages/netarch/topo-group/components/TopologyListDialog.vue b/src/views/ops/pages/netarch/topo-group/components/TopologyListDialog.vue index b92398f..a55bb37 100644 --- a/src/views/ops/pages/netarch/topo-group/components/TopologyListDialog.vue +++ b/src/views/ops/pages/netarch/topo-group/components/TopologyListDialog.vue @@ -327,7 +327,11 @@ const getTypeColor = (type?: string) => { // 格式化时间 const formatTime = (time?: string) => { - return time ? dayjs(time).format('YYYY-MM-DD HH:mm') : '-' + if (!time) return '-' + if (time.startsWith('0001-01-01')) return '-' + const d = dayjs(time) + if (!d.isValid() || d.year() <= 1970) return '-' + return d.format('YYYY-MM-DD HH:mm') }