From 0a33582541968c0622f06320c5b9cb1b5cd98235 Mon Sep 17 00:00:00 2001 From: hi2hi Date: Tue, 12 Aug 2025 13:17:01 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20=E7=9B=91=E6=8E=A7=E5=9B=BE?= =?UTF-8?q?=E8=A1=A8=E6=B7=BB=E5=8A=A0=E6=96=AD=E7=BA=BF=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.vue | 5 +- src/components/charts/line.js | 20 +++--- src/components/charts/line.vue | 13 ++-- .../server-detail/server-monitor.vue | 61 ++++++++++++------- src/views/composable/server-monitor.js | 6 +- 5 files changed, 64 insertions(+), 41 deletions(-) diff --git a/src/App.vue b/src/App.vue index 0be9a15..98c0518 100644 --- a/src/App.vue +++ b/src/App.vue @@ -42,10 +42,9 @@ provide('currentTime', currentTime); */ function refreshTime() { currentTime.value = Date.now(); - setTimeout(() => { - refreshTime(); - }, 1000); + window.requestAnimationFrame(refreshTime); } +refreshTime(); // 是否为Windows系统 const isWindows = /windows|win32/i.test(navigator.userAgent); diff --git a/src/components/charts/line.js b/src/components/charts/line.js index 5abcf89..f2c2ffe 100644 --- a/src/components/charts/line.js +++ b/src/components/charts/line.js @@ -18,11 +18,13 @@ use([ DataZoomComponent, ]); -export default ( - dateList, - valueList, - mode = 'dark', -) => { +export default (options) => { + const { + dateList, + valueList, + mode = 'dark', + connectNulls = true, + } = options || {}; const fontFamily = config.nazhua.disableSarasaTermSC === true ? undefined : 'Sarasa Term SC'; const option = { darkMode: mode === 'dark', @@ -36,7 +38,7 @@ export default ( let res = `

${time}

`; if (params.length < 10) { params.forEach((i) => { - res += `${i.marker} ${i.seriesName}: ${i.value[1]}ms
`; + res += i.value[1] ? `${i.marker} ${i.seriesName}: ${i.value[1]}ms
` : ''; }); } else { res += ''; @@ -45,7 +47,9 @@ export default ( if (index % 2 === 0) { res += ''; } - res += ``; + res += i.value[1] + ? `` + : ''; if (index % 2 === 1) { res += ''; trEnd = true; @@ -108,7 +112,7 @@ export default ( ...i, type: 'line', smooth: true, - connectNulls: true, + connectNulls, legendHoverLink: false, symbol: 'none', })), diff --git a/src/components/charts/line.vue b/src/components/charts/line.vue index 3d8d997..f9d1297 100644 --- a/src/components/charts/line.vue +++ b/src/components/charts/line.vue @@ -38,15 +38,20 @@ const props = defineProps({ type: [Number, String], default: null, }, + connectNulls: { + type: [Boolean, String], + default: true, + }, }); const chartRef = ref(); const option = computed(() => { if (props.dateList && props.valueList) { - return lineChart( - props.dateList, - props.valueList, - ); + return lineChart({ + dateList: props.dateList, + valueList: props.valueList, + connectNulls: props.connectNulls, + }); } return null; }); diff --git a/src/views/components/server-detail/server-monitor.vue b/src/views/components/server-detail/server-monitor.vue index 83e454b..dc493c7 100644 --- a/src/views/components/server-detail/server-monitor.vue +++ b/src/views/components/server-detail/server-monitor.vue @@ -118,10 +118,10 @@ {{ cateItem.avg }}ms - -ms + {{ cateItem.over }}% @@ -131,6 +131,7 @@ :date-list="monitorChartData.dateList" :value-list="[monitorChartData.valueList[index]]" :size="240" + :connect-nulls="false" /> @@ -168,12 +169,6 @@ > {{ cateItem.avg }}ms - - -ms - @@ -183,6 +178,7 @@ @@ -263,8 +259,9 @@ const monitorChartType = computed(() => { return config.nazhua.monitorChartType; }); -const now = ref(Date.now()); -const accpetShowTime = computed(() => now.value - (minute.value * 60 * 1000)); +// 服务器时间(后面来自接口) +const nowServerTime = computed(() => store.state.serverTime || Date.now()); +const accpetShowTime = computed(() => (Math.floor(nowServerTime.value / 60000) - minute.value) * 60000); const minuteActiveArrowStyle = computed(() => { const index = minutes.findIndex((i) => i.value === minute.value); @@ -302,13 +299,25 @@ const monitorChartData = computed(() => { }; } const showAvgDelay = []; - const showCreateTime = i.created_at.filter((o, index) => { + const showCreateTime = []; + const accpeTimeMap = {}; + i.created_at.forEach((o, index) => { const status = o >= accpetShowTime.value; if (status) { - showAvgDelay.push(i.avg_delay[index]); + accpeTimeMap[o] = i.avg_delay[index]; } - return status; }); + const allMintues = Math.floor((Date.now() - accpetShowTime.value) / 60000); + for (let j = 0; j < allMintues; j += 1) { + const time = accpetShowTime.value + j * 60000; + showCreateTime.push(time); + const timeProp = accpeTimeMap[time]; + if (timeProp) { + showAvgDelay.push(timeProp); + } else { + showAvgDelay.push(null); + } + } const { threshold, mean, @@ -316,19 +325,21 @@ const monitorChartData = computed(() => { min, } = peakShaving.value ? getThreshold(showAvgDelay, 2) : {}; showCreateTime.forEach((o, index) => { - if (dateMap[o]) { + if (Object.prototype.hasOwnProperty.call(dateMap, o)) { return; } const avgDelay = showAvgDelay[index]; if (peakShaving.value) { if (avgDelay === 0) { + dateMap[o] = null; return; } if (Math.abs(avgDelay - mean) > threshold && max / min > 2) { + dateMap[o] = null; return; } } - dateMap[o] = (avgDelay).toFixed(2) * 1; + dateMap[o] = avgDelay ? (avgDelay).toFixed(2) * 1 : null; }); }); let dateList = []; @@ -351,9 +362,9 @@ const monitorChartData = computed(() => { showCates.value[id] = true; } // 计算平均延迟和成功率 - const validAvgs = avgs.filter((a) => a[1] !== 0); + const validAvgs = avgs.filter((a) => a[1] !== 0 && a[1] !== null); const avg = validAvgs.reduce((a, b) => a + b[1], 0) / validAvgs.length; - const over = avgs.filter((a) => a[1] !== 0).length / avgs.length; + const over = avgs.filter((a) => a[1] !== 0 && a[1] !== null).length / avgs.length; const cateItem = { id, name: i, @@ -384,8 +395,7 @@ const monitorChartData = computed(() => { }); } }); - // 去重 - dateList = Array.from(new Set(dateList)).sort((a, b) => a - b); + dateList = dateList.sort((a, b) => a - b); valueList = valueList.filter((i) => showCates.value[i.id]); return { dateList, @@ -410,7 +420,6 @@ function switchChartType() { } function toggleMinute(value) { - now.value = store.state.serverTime || Date.now(); minute.value = value; } @@ -454,7 +463,6 @@ async function loadMonitor() { }).catch((err) => { console.error(err); }); - now.value = store.state.serverTime || Date.now(); } let loadMonitorTimer = null; @@ -539,8 +547,15 @@ onUnmounted(() => { color: #fff; } + .cate-over-rate { + height: var(--cate-item-height); + line-height: calc(var(--cate-item-height) + 2px); + text-align: right; + color: #fffbd8; + } + &.disabled { - filter: grayscale(1); + filter: grayscale(1) brightness(0.8); opacity: 0.5; } } diff --git a/src/views/composable/server-monitor.js b/src/views/composable/server-monitor.js index 0d62c1a..71a2ba1 100644 --- a/src/views/composable/server-monitor.js +++ b/src/views/composable/server-monitor.js @@ -11,15 +11,15 @@ import uniqolor from 'uniqolor'; */ export function getThreshold(data, tolerance = 2) { // 计算数据的平均值 - const mean = data.reduce((sum, value) => sum + value, 0) / data.length; + const mean = data.reduce((sum, value) => sum + (value || 0), 0) / data.length; // 计算数据的方差 - const variance = data.reduce((sum, value) => sum + (value - mean) ** 2, 0) / data.length; + const variance = data.reduce((sum, value) => sum + ((value || 0) - mean) ** 2, 0) / data.length; // 计算标准差 const stdDev = Math.sqrt(variance); // 计算阈值 const threshold = tolerance * stdDev; // 过滤掉值为0的数据 - const filteredData = data.filter((value) => value !== 0); + const filteredData = data.filter((value) => value !== 0 && value !== null); // 计算过滤后数据的最小值 const min = Math.min(...filteredData); // 计算过滤后数据的最大值
${i.marker} ${i.seriesName}: ${i.value[1]}ms${i.marker} ${i.seriesName}: ${i.value[1]}ms