diff --git a/src/store/index.js b/src/store/index.js
index 7336315..328d71f 100644
--- a/src/store/index.js
+++ b/src/store/index.js
@@ -18,6 +18,7 @@ import {
const defaultState = () => ({
init: false,
+ serverTime: 0,
serverGroup: [],
serverList: [],
serverCount: {
@@ -50,6 +51,9 @@ let firstSetServers = true;
const store = createStore({
state: defaultState(),
mutations: {
+ SET_SERVER_TIME(state, time) {
+ state.serverTime = time;
+ },
SET_SERVER_GROUP(state, serverGroup) {
state.serverGroup = serverGroup;
},
@@ -153,6 +157,9 @@ const store = createStore({
}) {
msg.on('servers', (res) => {
if (res) {
+ if (res.now) {
+ commit('SET_SERVER_TIME', res.now);
+ }
const servers = res.servers?.map?.((i) => {
const item = {
...i,
diff --git a/src/views/components/server-detail/server-monitor.vue b/src/views/components/server-detail/server-monitor.vue
index 8ba3d34..899f485 100644
--- a/src/views/components/server-detail/server-monitor.vue
+++ b/src/views/components/server-detail/server-monitor.vue
@@ -16,6 +16,7 @@
title="是否自动刷新"
@click="switchRefresh"
>
+ 刷新
- 刷新
- 削峰
+
+
+
+ 最近
+
+
+
+ {{ minuteItem.label }}
+
+
+
@@ -97,6 +119,7 @@ import {
onMounted,
onUnmounted,
} from 'vue';
+import { useStore } from 'vuex';
import config from '@/config';
import request from '@/utils/request';
import validate from '@/utils/validate';
@@ -115,12 +138,46 @@ const props = defineProps({
},
});
+const store = useStore();
+
+const minute = ref(1440);
+const minutes = [{
+ label: '30分钟',
+ value: 30,
+}, {
+ label: '1小时',
+ value: 60,
+}, {
+ label: '3小时',
+ value: 180,
+}, {
+ label: '6小时',
+ value: 360,
+}, {
+ label: '12小时',
+ value: 720,
+}, {
+ label: '24小时',
+ value: 1440,
+}];
const refreshData = ref(true);
const peakShaving = ref(false);
const showCates = ref({});
const monitorData = ref([]);
+const accpetShowTime = computed(() => {
+ const now = store.state.serverTime || Date.now();
+ return now - (minute.value * 60 * 1000);
+});
+
+const minuteActiveArrowStyle = computed(() => {
+ const index = minutes.findIndex((i) => i.value === minute.value);
+ return {
+ left: `calc(${index} * var(--minute-item-width))`,
+ };
+});
+
const monitorChartData = computed(() => {
/**
* 处理监控数据以生成分类的平均延迟随时间变化的列表。
@@ -149,17 +206,25 @@ const monitorChartData = computed(() => {
avgs: [],
};
}
+ const showAvgDelay = [];
+ const showCreateTime = i.created_at.filter((o, index) => {
+ const status = o >= accpetShowTime.value;
+ if (status) {
+ showAvgDelay.push(i.avg_delay[index]);
+ }
+ return status;
+ });
const {
threshold,
mean,
max,
min,
- } = peakShaving.value ? getThreshold(i.avg_delay, 2) : {};
- i.created_at.forEach((o, index) => {
+ } = peakShaving.value ? getThreshold(showAvgDelay, 2) : {};
+ showCreateTime.forEach((o, index) => {
if (dateMap[o]) {
return;
}
- const avgDelay = i.avg_delay[index];
+ const avgDelay = showAvgDelay[index];
if (peakShaving.value) {
if (avgDelay === 0) {
return;
@@ -238,6 +303,10 @@ function switchRefresh() {
refreshData.value = !refreshData.value;
}
+function toggleMinute(value) {
+ minute.value = value;
+}
+
function toggleShowCate(id) {
showCates.value[id] = !showCates.value[id];
}
@@ -299,12 +368,14 @@ onUnmounted(() => {
.module-head-group {
display: flex;
+ flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 10px;
- height: 30px;
.module-title {
+ width: max-content;
+ height: 30px;
line-height: 30px;
font-size: 16px;
color: #eee;
@@ -312,15 +383,16 @@ onUnmounted(() => {
.right-box {
display: flex;
+ flex-wrap: wrap;
align-items: center;
- gap: 10px;
+ gap: 12px;
}
.peak-shaving-group,
.refresh-data-group {
display: flex;
align-items: center;
- gap: 6px;
+ gap: 4px;
cursor: pointer;
@media screen and (max-width: 1024px) {
@@ -351,6 +423,7 @@ onUnmounted(() => {
.switch-dot {
left: 16px;
+ box-shadow: 1px 1px 2px #000;
}
}
}
@@ -360,6 +433,83 @@ onUnmounted(() => {
font-size: 12px;
}
}
+
+ .last-update-time-group {
+ --minute-item-width: 50px;
+ --minute-item-height: 20px;
+ display: flex;
+ align-items: center;
+ gap: 4px;
+
+ .last-update-time-label {
+ color: #ddd;
+ height: var(--minute-item-height);
+ line-height: var(--minute-item-height);
+ font-size: 12px;
+ }
+
+ @media screen and (max-width: 660px) {
+ --minute-item-width: 46px;
+ }
+
+ @media screen and (max-width: 600px) {
+ --minute-item-width: 46px;
+ }
+
+ @media screen and (max-width: 400px) {
+ .last-update-time-label {
+ display: none;
+ }
+ }
+
+ @media screen and (max-width: 330px) {
+ margin-left: -12px;
+ }
+
+ @media screen and (max-width: 320px) {
+ margin-left: -18px;
+ }
+ }
+ .minutes {
+ position: relative;
+ display: flex;
+ align-items: center;
+ // padding: 0 10px;
+ height: var(--minute-item-height);
+ background: rgba(#fff, 0.1);
+ border-radius: calc(var(--minute-item-height) / 2);
+
+ .minute-item {
+ position: relative;
+ z-index: 10;
+ width: var(--minute-item-width);
+ height: var(--minute-item-height);
+ line-height: var(--minute-item-height);
+ font-size: 12px;
+ text-align: center;
+ cursor: pointer;
+ color: #aaa;
+ transition: color 0.3s;
+
+ &.active {
+ color: #fff;
+ text-shadow: 1px 1px 2px #000;
+ }
+ }
+
+ .active-arrow {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: var(--minute-item-width);
+ height: var(--minute-item-height);
+ border-radius: calc(var(--minute-item-height) / 2);
+ background: #4caf50;
+ // opacity: 0.5;
+ transition: left 0.3s;
+ z-index: 1;
+ }
+ }
}
.monitor-cate-group {