From 0829dc9ce41cf7ef0023866072d0a70c2dadb252 Mon Sep 17 00:00:00 2001 From: shiran Date: Wed, 3 Jun 2026 18:28:12 +0800 Subject: [PATCH] =?UTF-8?q?feat(monitor):=20=E7=9B=91=E6=8E=A7=E6=97=B6?= =?UTF-8?q?=E9=97=B4=E9=80=89=E6=8B=A9=E5=99=A8=E7=BB=9F=E4=B8=80=E4=B8=BA?= =?UTF-8?q?=E7=9B=B8=E5=AF=B9=E6=97=B6=E9=97=B4+=E8=87=AA=E5=AE=9A?= =?UTF-8?q?=E4=B9=89=E8=8C=83=E5=9B=B4=E5=8F=8C=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - VmMonitor/VmDetail/UserVmDetail/HostDetail 四个页面统一改造 - 支持「最近」模式(动态计算时间范围)和「自定义」模式(固定日期范围) - 每小时流量图表同步应用双模式选择器 - 移除旧的 monitorShortcuts 快捷方式 Co-authored-by: Cursor --- src/views/user-vm/UserVmDetail.vue | 106 ++++++++++++++++-------- src/views/virtualization/HostDetail.vue | 62 ++++++++------ src/views/virtualization/VmDetail.vue | 106 ++++++++++++++++-------- src/views/virtualization/VmMonitor.vue | 55 +++++++++--- 4 files changed, 223 insertions(+), 106 deletions(-) diff --git a/src/views/user-vm/UserVmDetail.vue b/src/views/user-vm/UserVmDetail.vue index 122f2e8..4376499 100644 --- a/src/views/user-vm/UserVmDetail.vue +++ b/src/views/user-vm/UserVmDetail.vue @@ -402,15 +402,28 @@

监控指标

+ + 最近 + 自定义 + + + + + + + + + + 粒度: {{ currentIntervalLabel }} @@ -516,16 +529,27 @@

每小时流量

-
+
+ + 最近 + 自定义 + + + + + + + + 刷新 @@ -2259,24 +2283,35 @@ const submitAddTraffic = async () => { } // ---- 每小时流量统计 ---- +const trafficTimeMode = ref('relative') +const trafficRelativeMinutes = ref(1440) const trafficHourlyRange = ref(null) const trafficHourlyData = ref([]) const trafficHourlyLoading = ref(false) const trafficHourlyChartRef = ref(null) let trafficHourlyChart = null +const getTrafficTimeRange = () => { + if (trafficTimeMode.value === 'relative') { + const endTime = new Date() + const startTime = new Date(endTime - trafficRelativeMinutes.value * 60 * 1000) + return { startTime, endTime } + } else { + if (!trafficHourlyRange.value || trafficHourlyRange.value.length < 2) return null + return { startTime: new Date(trafficHourlyRange.value[0]), endTime: new Date(trafficHourlyRange.value[1]) } + } +} + const loadTrafficHourly = async () => { if (!userGoodsId.value) return - if (!trafficHourlyRange.value) { - const now = new Date() - trafficHourlyRange.value = [new Date(now.getTime() - 24 * 3600 * 1000), now] - } + const range = getTrafficTimeRange() + if (!range) return trafficHourlyLoading.value = true try { const res = await getUserVmTrafficHourly({ user_goods_id: userGoodsId.value, - start: new Date(trafficHourlyRange.value[0]).toISOString(), - end_time: new Date(trafficHourlyRange.value[1]).toISOString() + start: range.startTime.toISOString(), + end_time: range.endTime.toISOString() }) const raw = res?.data?.data?.data trafficHourlyData.value = typeof raw === 'string' ? JSON.parse(raw) : (Array.isArray(raw) ? raw : []) @@ -2372,21 +2407,20 @@ let trafficUsedChart = null const metricsData = ref(null) const metricsLoading = ref(false) -const makeDefaultRange = () => { - const now = new Date() - return [new Date(now.getTime() - 10 * 60 * 1000), now] -} -const monitorDateRange = ref(makeDefaultRange()) +const monitorTimeMode = ref('relative') +const monitorRelativeMinutes = ref(10) +const monitorDateRange = ref(null) -const monitorShortcuts = [ - { text: '最近10分钟', value: () => { const n = new Date(); return [new Date(n.getTime() - 10 * 60000), n] } }, - { text: '最近30分钟', value: () => { const n = new Date(); return [new Date(n.getTime() - 30 * 60000), n] } }, - { text: '最近1小时', value: () => { const n = new Date(); return [new Date(n.getTime() - 3600000), n] } }, - { text: '最近6小时', value: () => { const n = new Date(); return [new Date(n.getTime() - 6 * 3600000), n] } }, - { text: '最近12小时', value: () => { const n = new Date(); return [new Date(n.getTime() - 12 * 3600000), n] } }, - { text: '最近1天', value: () => { const n = new Date(); return [new Date(n.getTime() - 86400000), n] } }, - { text: '最近7天', value: () => { const n = new Date(); return [new Date(n.getTime() - 7 * 86400000), n] } }, -] +const getMonitorTimeRange = () => { + if (monitorTimeMode.value === 'relative') { + const endTime = new Date() + const startTime = new Date(endTime - monitorRelativeMinutes.value * 60 * 1000) + return { startTime, endTime } + } else { + if (!monitorDateRange.value || monitorDateRange.value.length < 2) return null + return { startTime: new Date(monitorDateRange.value[0]), endTime: new Date(monitorDateRange.value[1]) } + } +} function calcInterval(startTime, endTime) { const spanMin = (endTime.getTime() - startTime.getTime()) / 60000 @@ -2404,8 +2438,9 @@ function calcInterval(startTime, endTime) { const intervalLabelMap = { '1m': '1分钟', '3m': '3分钟', '5m': '5分钟', '15m': '15分钟', '30m': '30分钟', '1h': '1小时', '2h': '2小时', '6h': '6小时', '12h': '12小时', '1d': '1天' } const currentIntervalLabel = computed(() => { - if (!monitorDateRange.value || monitorDateRange.value.length < 2) return '-' - const iv = calcInterval(new Date(monitorDateRange.value[0]), new Date(monitorDateRange.value[1])) + const range = getMonitorTimeRange() + if (!range) return '-' + const iv = calcInterval(range.startTime, range.endTime) return intervalLabelMap[iv] || iv }) @@ -2450,11 +2485,11 @@ const vmMemPercent = (m) => { const loadMetricsHistory = async () => { if (!userGoodsId.value) return - if (!monitorDateRange.value || monitorDateRange.value.length < 2) return + const range = getMonitorTimeRange() + if (!range) return metricsLoading.value = true try { - const startTime = new Date(monitorDateRange.value[0]) - const endTime = new Date(monitorDateRange.value[1]) + const { startTime, endTime } = range const interval = calcInterval(startTime, endTime) const params = { user_goods_id: userGoodsId.value, @@ -2482,7 +2517,8 @@ const renderMetricsCharts = () => { const metrics = metricsData.value if (!Array.isArray(metrics) || !metrics.length) return - const spanMs = monitorDateRange.value ? (new Date(monitorDateRange.value[1]).getTime() - new Date(monitorDateRange.value[0]).getTime()) : 600000 + const range = getMonitorTimeRange() + const spanMs = range ? (range.endTime.getTime() - range.startTime.getTime()) : 600000 const showDate = spanMs >= 86400000 const symbolType = metrics.length < 30 ? 'circle' : 'none' const labelRotate = showDate ? 45 : 0 diff --git a/src/views/virtualization/HostDetail.vue b/src/views/virtualization/HostDetail.vue index 857d576..9ad4e20 100644 --- a/src/views/virtualization/HostDetail.vue +++ b/src/views/virtualization/HostDetail.vue @@ -259,15 +259,28 @@

监控指标

+ + 最近 + 自定义 + + + + + + + + + + 粒度: {{ currentIntervalLabel }} @@ -1109,21 +1122,20 @@ const latestMetrics = computed(() => { return arr[arr.length - 1] }) -const makeDefaultRange = () => { - const now = new Date() - return [new Date(now.getTime() - 10 * 60 * 1000), now] -} -const monitorDateRange = ref(makeDefaultRange()) +const monitorTimeMode = ref('relative') +const monitorRelativeMinutes = ref(10) +const monitorDateRange = ref(null) -const monitorShortcuts = [ - { text: '最近10分钟', value: () => { const n = new Date(); return [new Date(n.getTime() - 10 * 60000), n] } }, - { text: '最近30分钟', value: () => { const n = new Date(); return [new Date(n.getTime() - 30 * 60000), n] } }, - { text: '最近1小时', value: () => { const n = new Date(); return [new Date(n.getTime() - 3600000), n] } }, - { text: '最近6小时', value: () => { const n = new Date(); return [new Date(n.getTime() - 6 * 3600000), n] } }, - { text: '最近12小时', value: () => { const n = new Date(); return [new Date(n.getTime() - 12 * 3600000), n] } }, - { text: '最近1天', value: () => { const n = new Date(); return [new Date(n.getTime() - 86400000), n] } }, - { text: '最近7天', value: () => { const n = new Date(); return [new Date(n.getTime() - 7 * 86400000), n] } }, -] +const getMonitorTimeRange = () => { + if (monitorTimeMode.value === 'relative') { + const endTime = new Date() + const startTime = new Date(endTime - monitorRelativeMinutes.value * 60 * 1000) + return { startTime, endTime } + } else { + if (!monitorDateRange.value || monitorDateRange.value.length < 2) return null + return { startTime: new Date(monitorDateRange.value[0]), endTime: new Date(monitorDateRange.value[1]) } + } +} function calcInterval(startTime, endTime) { const spanMin = (endTime.getTime() - startTime.getTime()) / 60000 @@ -1141,18 +1153,19 @@ function calcInterval(startTime, endTime) { const intervalLabelMap = { '1m': '1分钟', '3m': '3分钟', '5m': '5分钟', '15m': '15分钟', '30m': '30分钟', '1h': '1小时', '2h': '2小时', '6h': '6小时', '12h': '12小时', '1d': '1天' } const currentIntervalLabel = computed(() => { - if (!monitorDateRange.value || monitorDateRange.value.length < 2) return '-' - const iv = calcInterval(new Date(monitorDateRange.value[0]), new Date(monitorDateRange.value[1])) + const range = getMonitorTimeRange() + if (!range) return '-' + const iv = calcInterval(range.startTime, range.endTime) return intervalLabelMap[iv] || iv }) const loadHistoricalMetrics = async () => { if (!serviceId.value || !hostId.value) return - if (!monitorDateRange.value || monitorDateRange.value.length < 2) return + const range = getMonitorTimeRange() + if (!range) return historicalMetricsLoading.value = true try { - const startTime = new Date(monitorDateRange.value[0]) - const endTime = new Date(monitorDateRange.value[1]) + const { startTime, endTime } = range const interval = calcInterval(startTime, endTime) const params = { service_id: serviceId.value, @@ -1192,7 +1205,8 @@ const renderHistoricalCharts = () => { const metrics = historicalMetricsData.value if (!Array.isArray(metrics) || !metrics.length) return - const spanMs = monitorDateRange.value ? (new Date(monitorDateRange.value[1]).getTime() - new Date(monitorDateRange.value[0]).getTime()) : 0 + const range = getMonitorTimeRange() + const spanMs = range ? (range.endTime.getTime() - range.startTime.getTime()) : 0 const showDate = spanMs >= 12 * 3600 * 1000 const symbolType = spanMs >= 7 * 86400 * 1000 ? 'circle' : 'none' const labelRotate = showDate ? 45 : 0 diff --git a/src/views/virtualization/VmDetail.vue b/src/views/virtualization/VmDetail.vue index 717cc47..da87bc2 100644 --- a/src/views/virtualization/VmDetail.vue +++ b/src/views/virtualization/VmDetail.vue @@ -594,15 +594,28 @@

监控指标

+ + 最近 + 自定义 + + + + + + + + + + 粒度: {{ currentIntervalLabel }} @@ -708,16 +721,27 @@

每小时流量

-
+
+ + 最近 + 自定义 + + + + + + + + 刷新 @@ -1911,21 +1935,20 @@ let isPageActive = false const historicalMetricsData = ref(null) const historicalMetricsLoading = ref(false) -const makeDefaultRange = () => { - const now = new Date() - return [new Date(now.getTime() - 10 * 60 * 1000), now] -} -const monitorDateRange = ref(makeDefaultRange()) +const monitorTimeMode = ref('relative') +const monitorRelativeMinutes = ref(10) +const monitorDateRange = ref(null) -const monitorShortcuts = [ - { text: '最近10分钟', value: () => { const n = new Date(); return [new Date(n.getTime() - 10 * 60000), n] } }, - { text: '最近30分钟', value: () => { const n = new Date(); return [new Date(n.getTime() - 30 * 60000), n] } }, - { text: '最近1小时', value: () => { const n = new Date(); return [new Date(n.getTime() - 3600000), n] } }, - { text: '最近6小时', value: () => { const n = new Date(); return [new Date(n.getTime() - 6 * 3600000), n] } }, - { text: '最近12小时', value: () => { const n = new Date(); return [new Date(n.getTime() - 12 * 3600000), n] } }, - { text: '最近1天', value: () => { const n = new Date(); return [new Date(n.getTime() - 86400000), n] } }, - { text: '最近7天', value: () => { const n = new Date(); return [new Date(n.getTime() - 7 * 86400000), n] } }, -] +const getMonitorTimeRange = () => { + if (monitorTimeMode.value === 'relative') { + const endTime = new Date() + const startTime = new Date(endTime - monitorRelativeMinutes.value * 60 * 1000) + return { startTime, endTime } + } else { + if (!monitorDateRange.value || monitorDateRange.value.length < 2) return null + return { startTime: new Date(monitorDateRange.value[0]), endTime: new Date(monitorDateRange.value[1]) } + } +} function calcInterval(startTime, endTime) { const spanMin = (endTime.getTime() - startTime.getTime()) / 60000 @@ -1943,8 +1966,9 @@ function calcInterval(startTime, endTime) { const intervalLabelMap = { '1m': '1分钟', '3m': '3分钟', '5m': '5分钟', '15m': '15分钟', '30m': '30分钟', '1h': '1小时', '2h': '2小时', '6h': '6小时', '12h': '12小时', '1d': '1天' } const currentIntervalLabel = computed(() => { - if (!monitorDateRange.value || monitorDateRange.value.length < 2) return '-' - const iv = calcInterval(new Date(monitorDateRange.value[0]), new Date(monitorDateRange.value[1])) + const range = getMonitorTimeRange() + if (!range) return '-' + const iv = calcInterval(range.startTime, range.endTime) return intervalLabelMap[iv] || iv }) @@ -1987,11 +2011,11 @@ const vmMemPercent = (m) => { const loadHistoricalMetrics = async () => { if (!serviceId.value || !detail.value?.name) return - if (!monitorDateRange.value || monitorDateRange.value.length < 2) return + const range = getMonitorTimeRange() + if (!range) return historicalMetricsLoading.value = true try { - const startTime = new Date(monitorDateRange.value[0]) - const endTime = new Date(monitorDateRange.value[1]) + const { startTime, endTime } = range const interval = calcInterval(startTime, endTime) const params = { service_id: serviceId.value, @@ -2021,7 +2045,8 @@ const renderHistoricalCharts = () => { const metrics = historicalMetricsData.value if (!Array.isArray(metrics) || !metrics.length) return - const spanMs = monitorDateRange.value ? (new Date(monitorDateRange.value[1]).getTime() - new Date(monitorDateRange.value[0]).getTime()) : 0 + const range = getMonitorTimeRange() + const spanMs = range ? (range.endTime.getTime() - range.startTime.getTime()) : 0 const showDate = spanMs >= 12 * 3600 * 1000 const symbolType = showDate ? 'circle' : 'none' const labelRotate = showDate ? 45 : 0 @@ -3003,26 +3028,37 @@ const handleConnectNetwork = () => { } // ---- 每小时流量统计 ---- +const trafficTimeMode = ref('relative') +const trafficRelativeMinutes = ref(1440) const trafficHourlyRange = ref(null) const trafficHourlyData = ref([]) const trafficHourlyLoading = ref(false) const trafficHourlyChartRef = ref(null) let trafficHourlyChart = null +const getTrafficTimeRange = () => { + if (trafficTimeMode.value === 'relative') { + const endTime = new Date() + const startTime = new Date(endTime - trafficRelativeMinutes.value * 60 * 1000) + return { startTime, endTime } + } else { + if (!trafficHourlyRange.value || trafficHourlyRange.value.length < 2) return null + return { startTime: new Date(trafficHourlyRange.value[0]), endTime: new Date(trafficHourlyRange.value[1]) } + } +} + const loadTrafficHourly = async () => { if (!serviceId.value || !detail.value?.host_id || !detail.value?.name) return - if (!trafficHourlyRange.value) { - const now = new Date() - trafficHourlyRange.value = [new Date(now.getTime() - 24 * 3600 * 1000), now] - } + const range = getTrafficTimeRange() + if (!range) return trafficHourlyLoading.value = true try { const res = await getVmTrafficHourly({ service_id: serviceId.value, host_id: detail.value.host_id, vm_name: detail.value.name, - start: new Date(trafficHourlyRange.value[0]).toISOString(), - end_time: new Date(trafficHourlyRange.value[1]).toISOString() + start: range.startTime.toISOString(), + end_time: range.endTime.toISOString() }) const raw = res?.data?.data?.data trafficHourlyData.value = typeof raw === 'string' ? JSON.parse(raw) : (Array.isArray(raw) ? raw : []) diff --git a/src/views/virtualization/VmMonitor.vue b/src/views/virtualization/VmMonitor.vue index 5932881..6beecfb 100644 --- a/src/views/virtualization/VmMonitor.vue +++ b/src/views/virtualization/VmMonitor.vue @@ -32,19 +32,34 @@
时间 - - - - - - - - + + 最近 + 自定义 + + + + + + + + + +
自动刷新 - + @@ -91,7 +106,9 @@ const selectedMetrics = ref(['cpu', 'memory']) const metricsLoading = ref(false) const loaded = ref(false) const metricsDataMap = ref({}) +const timeMode = ref('relative') const timeRange = ref(60) +const fixedDateRange = ref(null) const autoRefreshInterval = ref(0) let autoRefreshTimer = null @@ -169,11 +186,23 @@ const loadVmList = async () => { } } +const getTimeRange = () => { + if (timeMode.value === 'relative') { + const endTime = new Date() + const startTime = new Date(endTime - timeRange.value * 60 * 1000) + return { startTime, endTime } + } else { + if (!fixedDateRange.value || fixedDateRange.value.length < 2) return null + return { startTime: new Date(fixedDateRange.value[0]), endTime: new Date(fixedDateRange.value[1]) } + } +} + const handleRefresh = async () => { if (!selectedVms.value.length) return + const range = getTimeRange() + if (!range) { ElMessage.warning('请选择时间范围'); return } metricsLoading.value = true - const endTime = new Date() - const startTime = new Date(endTime - timeRange.value * 60 * 1000) + const { startTime, endTime } = range const interval = calcInterval(endTime - startTime) const dataMap = {} @@ -208,7 +237,9 @@ const handleRefresh = async () => { } const renderCharts = () => { - const showDate = timeRange.value >= 720 + const range = getTimeRange() + const spanMs = range ? (range.endTime - range.startTime) : 0 + const showDate = spanMs >= 12 * 3600 * 1000 const labelRotate = showDate ? 30 : 0 for (const metric of selectedMetrics.value) {