feat(monitor): 监控时间选择器统一为相对时间+自定义范围双模式
- VmMonitor/VmDetail/UserVmDetail/HostDetail 四个页面统一改造 - 支持「最近」模式(动态计算时间范围)和「自定义」模式(固定日期范围) - 每小时流量图表同步应用双模式选择器 - 移除旧的 monitorShortcuts 快捷方式 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -402,15 +402,28 @@
|
||||
<div class="section-header">
|
||||
<h3 class="section-title">监控指标</h3>
|
||||
<div style="display: flex; align-items: center; gap: 8px; flex-wrap: wrap">
|
||||
<el-radio-group v-model="monitorTimeMode" size="small" @change="loadMetricsHistory">
|
||||
<el-radio-button label="relative">最近</el-radio-button>
|
||||
<el-radio-button label="fixed">自定义</el-radio-button>
|
||||
</el-radio-group>
|
||||
<el-select v-if="monitorTimeMode === 'relative'" v-model="monitorRelativeMinutes" size="small" style="width: 120px" @change="loadMetricsHistory">
|
||||
<el-option label="10分钟" :value="10" />
|
||||
<el-option label="30分钟" :value="30" />
|
||||
<el-option label="1小时" :value="60" />
|
||||
<el-option label="6小时" :value="360" />
|
||||
<el-option label="12小时" :value="720" />
|
||||
<el-option label="1天" :value="1440" />
|
||||
<el-option label="7天" :value="10080" />
|
||||
</el-select>
|
||||
<el-date-picker
|
||||
v-else
|
||||
v-model="monitorDateRange"
|
||||
type="datetimerange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
start-placeholder="开始"
|
||||
end-placeholder="结束"
|
||||
size="small"
|
||||
style="width: 360px"
|
||||
:shortcuts="monitorShortcuts"
|
||||
style="width: 340px"
|
||||
@change="loadMetricsHistory"
|
||||
/>
|
||||
<span style="font-size:12px;color:#909399;white-space:nowrap">粒度: {{ currentIntervalLabel }}</span>
|
||||
@@ -516,16 +529,27 @@
|
||||
<div class="section-block" style="margin-top:16px">
|
||||
<div class="section-header">
|
||||
<h3 class="section-title">每小时流量</h3>
|
||||
<div style="display: flex; align-items: center; gap: 8px">
|
||||
<div style="display: flex; align-items: center; gap: 8px; flex-wrap: wrap">
|
||||
<el-radio-group v-model="trafficTimeMode" size="small" @change="loadTrafficHourly">
|
||||
<el-radio-button label="relative">最近</el-radio-button>
|
||||
<el-radio-button label="fixed">自定义</el-radio-button>
|
||||
</el-radio-group>
|
||||
<el-select v-if="trafficTimeMode === 'relative'" v-model="trafficRelativeMinutes" size="small" style="width: 120px" @change="loadTrafficHourly">
|
||||
<el-option label="6小时" :value="360" />
|
||||
<el-option label="12小时" :value="720" />
|
||||
<el-option label="1天" :value="1440" />
|
||||
<el-option label="3天" :value="4320" />
|
||||
<el-option label="7天" :value="10080" />
|
||||
</el-select>
|
||||
<el-date-picker
|
||||
v-else
|
||||
v-model="trafficHourlyRange"
|
||||
type="datetimerange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
start-placeholder="开始"
|
||||
end-placeholder="结束"
|
||||
size="small"
|
||||
style="width: 360px"
|
||||
:shortcuts="monitorShortcuts"
|
||||
style="width: 340px"
|
||||
@change="loadTrafficHourly"
|
||||
/>
|
||||
<el-button size="small" :icon="Refresh" @click="loadTrafficHourly" :loading="trafficHourlyLoading">刷新</el-button>
|
||||
@@ -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
|
||||
|
||||
@@ -259,15 +259,28 @@
|
||||
<div class="section-header">
|
||||
<h3 class="section-title">监控指标</h3>
|
||||
<div style="display: flex; align-items: center; gap: 8px; flex-wrap: wrap">
|
||||
<el-radio-group v-model="monitorTimeMode" size="small" @change="loadHistoricalMetrics">
|
||||
<el-radio-button label="relative">最近</el-radio-button>
|
||||
<el-radio-button label="fixed">自定义</el-radio-button>
|
||||
</el-radio-group>
|
||||
<el-select v-if="monitorTimeMode === 'relative'" v-model="monitorRelativeMinutes" size="small" style="width: 120px" @change="loadHistoricalMetrics">
|
||||
<el-option label="10分钟" :value="10" />
|
||||
<el-option label="30分钟" :value="30" />
|
||||
<el-option label="1小时" :value="60" />
|
||||
<el-option label="6小时" :value="360" />
|
||||
<el-option label="12小时" :value="720" />
|
||||
<el-option label="1天" :value="1440" />
|
||||
<el-option label="7天" :value="10080" />
|
||||
</el-select>
|
||||
<el-date-picker
|
||||
v-else
|
||||
v-model="monitorDateRange"
|
||||
type="datetimerange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
start-placeholder="开始"
|
||||
end-placeholder="结束"
|
||||
size="small"
|
||||
style="width: 360px"
|
||||
:shortcuts="monitorShortcuts"
|
||||
style="width: 340px"
|
||||
@change="loadHistoricalMetrics"
|
||||
/>
|
||||
<span style="font-size:12px;color:#909399;white-space:nowrap">粒度: {{ currentIntervalLabel }}</span>
|
||||
@@ -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
|
||||
|
||||
@@ -594,15 +594,28 @@
|
||||
<div class="section-header">
|
||||
<h3 class="section-title">监控指标</h3>
|
||||
<div style="display: flex; align-items: center; gap: 8px; flex-wrap: wrap">
|
||||
<el-radio-group v-model="monitorTimeMode" size="small" @change="loadHistoricalMetrics">
|
||||
<el-radio-button label="relative">最近</el-radio-button>
|
||||
<el-radio-button label="fixed">自定义</el-radio-button>
|
||||
</el-radio-group>
|
||||
<el-select v-if="monitorTimeMode === 'relative'" v-model="monitorRelativeMinutes" size="small" style="width: 120px" @change="loadHistoricalMetrics">
|
||||
<el-option label="10分钟" :value="10" />
|
||||
<el-option label="30分钟" :value="30" />
|
||||
<el-option label="1小时" :value="60" />
|
||||
<el-option label="6小时" :value="360" />
|
||||
<el-option label="12小时" :value="720" />
|
||||
<el-option label="1天" :value="1440" />
|
||||
<el-option label="7天" :value="10080" />
|
||||
</el-select>
|
||||
<el-date-picker
|
||||
v-else
|
||||
v-model="monitorDateRange"
|
||||
type="datetimerange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
start-placeholder="开始"
|
||||
end-placeholder="结束"
|
||||
size="small"
|
||||
style="width: 360px"
|
||||
:shortcuts="monitorShortcuts"
|
||||
style="width: 340px"
|
||||
@change="loadHistoricalMetrics"
|
||||
/>
|
||||
<span style="font-size:12px;color:#909399;white-space:nowrap">粒度: {{ currentIntervalLabel }}</span>
|
||||
@@ -708,16 +721,27 @@
|
||||
<div class="section-block">
|
||||
<div class="section-header">
|
||||
<h3 class="section-title">每小时流量</h3>
|
||||
<div style="display: flex; align-items: center; gap: 8px">
|
||||
<div style="display: flex; align-items: center; gap: 8px; flex-wrap: wrap">
|
||||
<el-radio-group v-model="trafficTimeMode" size="small" @change="loadTrafficHourly">
|
||||
<el-radio-button label="relative">最近</el-radio-button>
|
||||
<el-radio-button label="fixed">自定义</el-radio-button>
|
||||
</el-radio-group>
|
||||
<el-select v-if="trafficTimeMode === 'relative'" v-model="trafficRelativeMinutes" size="small" style="width: 120px" @change="loadTrafficHourly">
|
||||
<el-option label="6小时" :value="360" />
|
||||
<el-option label="12小时" :value="720" />
|
||||
<el-option label="1天" :value="1440" />
|
||||
<el-option label="3天" :value="4320" />
|
||||
<el-option label="7天" :value="10080" />
|
||||
</el-select>
|
||||
<el-date-picker
|
||||
v-else
|
||||
v-model="trafficHourlyRange"
|
||||
type="datetimerange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
start-placeholder="开始"
|
||||
end-placeholder="结束"
|
||||
size="small"
|
||||
style="width: 360px"
|
||||
:shortcuts="monitorShortcuts"
|
||||
style="width: 340px"
|
||||
@change="loadTrafficHourly"
|
||||
/>
|
||||
<el-button size="small" :icon="Refresh" @click="loadTrafficHourly" :loading="trafficHourlyLoading">刷新</el-button>
|
||||
@@ -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 : [])
|
||||
|
||||
@@ -32,19 +32,34 @@
|
||||
<div class="toolbar-row">
|
||||
<div class="toolbar-item">
|
||||
<span class="toolbar-label">时间</span>
|
||||
<el-select v-model="timeRange" style="width: 140px" @change="handleRefresh">
|
||||
<el-option label="最近10分钟" :value="10" />
|
||||
<el-option label="最近30分钟" :value="30" />
|
||||
<el-option label="最近1小时" :value="60" />
|
||||
<el-option label="最近3小时" :value="180" />
|
||||
<el-option label="最近6小时" :value="360" />
|
||||
<el-option label="最近12小时" :value="720" />
|
||||
<el-option label="最近24小时" :value="1440" />
|
||||
<el-radio-group v-model="timeMode" size="small" @change="handleRefresh">
|
||||
<el-radio-button label="relative">最近</el-radio-button>
|
||||
<el-radio-button label="fixed">自定义</el-radio-button>
|
||||
</el-radio-group>
|
||||
<el-select v-if="timeMode === 'relative'" v-model="timeRange" style="width: 130px" @change="handleRefresh">
|
||||
<el-option label="10分钟" :value="10" />
|
||||
<el-option label="30分钟" :value="30" />
|
||||
<el-option label="1小时" :value="60" />
|
||||
<el-option label="3小时" :value="180" />
|
||||
<el-option label="6小时" :value="360" />
|
||||
<el-option label="12小时" :value="720" />
|
||||
<el-option label="24小时" :value="1440" />
|
||||
</el-select>
|
||||
<el-date-picker
|
||||
v-else
|
||||
v-model="fixedDateRange"
|
||||
type="datetimerange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始"
|
||||
end-placeholder="结束"
|
||||
size="small"
|
||||
style="width: 340px"
|
||||
@change="handleRefresh"
|
||||
/>
|
||||
</div>
|
||||
<div class="toolbar-item">
|
||||
<span class="toolbar-label">自动刷新</span>
|
||||
<el-select v-model="autoRefreshInterval" style="width: 120px" @change="resetAutoRefresh">
|
||||
<el-select v-model="autoRefreshInterval" style="width: 110px" @change="resetAutoRefresh">
|
||||
<el-option label="关闭" :value="0" />
|
||||
<el-option label="10秒" :value="10" />
|
||||
<el-option label="30秒" :value="30" />
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user