告警趋势资源复用加载问题修复,新增30天告警分布

This commit is contained in:
龚皓 2024-10-16 14:48:22 +08:00
parent 8184d3ca94
commit 93f9af1c99
1 changed files with 277 additions and 99 deletions

View File

@ -9,27 +9,42 @@
<el-tab-pane label="本周" name="second"> <el-tab-pane label="本周" name="second">
<div ref="weekLineChartDom" class="chart-container"></div> <!-- 本周图表 --> <div ref="weekLineChartDom" class="chart-container"></div> <!-- 本周图表 -->
</el-tab-pane> </el-tab-pane>
<el-tab-pane label="最近30天" name="third"> <!-- 新增最近30天选项卡 -->
<div ref="monthLineChartDom" class="chart-container"></div> <!-- 最近30天图表 -->
</el-tab-pane>
</el-tabs> </el-tabs>
</el-card> </el-card>
</div> </div>
</template> </template>
<script setup> <script setup>
import { ref, onMounted, watch } from 'vue'; import { ref, onMounted, watch, nextTick } from 'vue';
import * as echarts from 'echarts'; import * as echarts from 'echarts';
import { BoxApi } from '@/utils/boxApi.ts';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { BoxApi } from '@/utils/boxApi.ts'; // 使 BoxApi import { debounce } from 'lodash';
const activeName = ref('first'); const activeName = ref('first');
const hourlyCounts = ref(Array(24).fill(0)); // 24 const hourlyCounts = ref(Array(24).fill(0)); // 24
const chartInstanceToday = ref(null);
const todayLineChartDom = ref(null);
const dailyCounts = ref(Array(7).fill(0)); // 7 const dailyCounts = ref(Array(7).fill(0)); // 7
const weekDates = ref([]); // 7 const weekDates = ref([]); // 7
const chartInstanceToday = ref(null);
const chartInstanceWeek = ref(null); const chartInstanceWeek = ref(null);
const todayLineChartDom = ref(null);
const weekLineChartDom = ref(null); const weekLineChartDom = ref(null);
const apiInstance = new BoxApi(); // BoxApi const monthlyCounts = ref(Array(30).fill(0)); // 30
const monthDates = ref([]); // 30
const chartInstanceMonth = ref(null); // 30
const monthLineChartDom = ref(null);
const apiInstance = new BoxApi();
const allEvents = ref([]); // 使 allEvents
let isTodayChartInitialized = false;
let isWeekChartInitialized = false;
let isMonthChartInitialized = false; // 30
// 7 // 7
const calculateWeekDates = () => { const calculateWeekDates = () => {
@ -37,6 +52,84 @@ const calculateWeekDates = () => {
weekDates.value = Array.from({ length: 7 }, (_, i) => today.subtract(i, 'day').format('YYYY-MM-DD')).reverse(); weekDates.value = Array.from({ length: 7 }, (_, i) => today.subtract(i, 'day').format('YYYY-MM-DD')).reverse();
}; };
// 30
const calculateMonthDates = () => {
const today = dayjs();
monthDates.value = Array.from({ length: 30 }, (_, i) => today.subtract(i, 'day').format('YYYY-MM-DD')).reverse();
};
//
const initTodayChart = () => {
if (todayLineChartDom.value) {
chartInstanceToday.value = echarts.init(todayLineChartDom.value);
const option = {
title: {
text: '今日告警数量趋势',
top: '3%',
textStyle: {
color: '#fff',
fontWeight: 'bold',
},
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
},
formatter: function (params) {
const { value, name } = params[0];
return `${name}<br/>告警数量: ${value}`;
},
textStyle: {
color: '#fff',
fontSize: 12,
},
},
xAxis: {
type: 'category',
boundaryGap: false,
data: Array.from({ length: 24 }, (_, i) => `${i.toString().padStart(2, '0')}:00`),
axisLabel: {
rotate: 0,
color: '#fff',
},
},
yAxis: {
type: 'value',
min: 0,
minInterval: 1,
axisLabel: {
color: '#fff',
},
},
series: [
{
name: '告警数量',
type: 'line',
data: hourlyCounts.value.length > 0 ? hourlyCounts.value : Array(24).fill(0),
smooth: true,
label: {
show: true,
position: 'top',
textStyle: {
color: '#fff',
},
},
lineStyle: {
color: 'rgb(60,178,239)', // 线
width: 2,
},
areaStyle: {
color: 'rgba(74, 172, 178, 0.3)', //
},
},
],
};
chartInstanceToday.value.setOption(option);
}
};
// //
const initWeekChart = () => { const initWeekChart = () => {
if (weekLineChartDom.value) { if (weekLineChartDom.value) {
@ -49,13 +142,13 @@ const initWeekChart = () => {
textStyle: { textStyle: {
color: '#fff', color: '#fff',
fontSize: 18, fontSize: 18,
fontWeight: 'bold' fontWeight: 'bold',
} },
}, },
tooltip: { tooltip: {
trigger: 'axis', trigger: 'axis',
axisPointer: { axisPointer: {
type: 'cross' type: 'cross',
}, },
formatter: function (params) { formatter: function (params) {
const { value } = params[0]; const { value } = params[0];
@ -63,8 +156,8 @@ const initWeekChart = () => {
}, },
textStyle: { textStyle: {
color: '#fff', color: '#fff',
fontSize: 12 fontSize: 12,
} },
}, },
xAxis: { xAxis: {
type: 'category', type: 'category',
@ -74,8 +167,8 @@ const initWeekChart = () => {
rotate: 0, rotate: 0,
interval: 0, interval: 0,
color: '#fff', color: '#fff',
fontSize: 12 fontSize: 12,
} },
}, },
yAxis: { yAxis: {
type: 'value', type: 'value',
@ -83,8 +176,8 @@ const initWeekChart = () => {
minInterval: 1, minInterval: 1,
axisLabel: { axisLabel: {
color: '#fff', color: '#fff',
fontSize: 12 fontSize: 12,
} },
}, },
series: [ series: [
{ {
@ -97,35 +190,42 @@ const initWeekChart = () => {
position: 'top', position: 'top',
textStyle: { textStyle: {
color: '#fff', color: '#fff',
fontSize: 12 fontSize: 12,
} },
} },
} lineStyle: {
] color: 'rgb(60,178,239)', // 线
width: 2,
},
areaStyle: {
color: 'rgba(74, 172, 178, 0.3)', //
},
},
],
}; };
chartInstanceWeek.value.setOption(option); chartInstanceWeek.value.setOption(option);
} }
}; };
// // 30
const initTodayChart = () => { const initMonthChart = () => {
if (todayLineChartDom.value) { if (monthLineChartDom.value) {
chartInstanceToday.value = echarts.init(todayLineChartDom.value); chartInstanceMonth.value = echarts.init(monthLineChartDom.value);
const option = { const option = {
title: { title: {
text: '今日告警数量趋势', text: '最近30天告警数量趋势',
top: '3%', top: '3%',
textStyle: { textStyle: {
color: '#fff', color: '#fff',
fontWeight: 'bold' fontWeight: 'bold',
} },
}, },
tooltip: { tooltip: {
trigger: 'axis', trigger: 'axis',
axisPointer: { axisPointer: {
type: 'cross' type: 'cross',
}, },
formatter: function (params) { formatter: function (params) {
const { value, name } = params[0]; const { value, name } = params[0];
@ -133,17 +233,17 @@ const initTodayChart = () => {
}, },
textStyle: { textStyle: {
color: '#fff', color: '#fff',
fontSize: 12 fontSize: 12,
} },
}, },
xAxis: { xAxis: {
type: 'category', type: 'category',
boundaryGap: false, boundaryGap: false,
data: Array.from({ length: 24 }, (_, i) => `${i.toString().padStart(2, '0')}:00`), data: monthDates.value,
axisLabel: { axisLabel: {
rotate: 0, rotate: 0,
color: '#fff', color: '#fff',
} },
}, },
yAxis: { yAxis: {
type: 'value', type: 'value',
@ -151,38 +251,44 @@ const initTodayChart = () => {
minInterval: 1, minInterval: 1,
axisLabel: { axisLabel: {
color: '#fff', color: '#fff',
} },
}, },
series: [ series: [
{ {
name: '告警数量', name: '告警数量',
type: 'line', type: 'line',
data: hourlyCounts.value.length > 0 ? hourlyCounts.value : Array(24).fill(0), data: monthlyCounts.value.length > 0 ? monthlyCounts.value : Array(30).fill(0),
smooth: true, smooth: true,
label: { label: {
show: true, show: true,
position: 'top', position: 'top',
textStyle: { textStyle: {
color: '#fff', color: '#fff',
} },
} },
} lineStyle: {
] color: 'rgb(60,178,239)', // 线
width: 2,
},
areaStyle: {
color: 'rgba(74, 172, 178, 0.3)', //
},
},
],
}; };
chartInstanceToday.value.setOption(option); chartInstanceMonth.value.setOption(option);
} }
}; };
// //
const fetchAndProcessEvents = async (token) => { const fetchAndProcessEvents = async (token) => {
try { try {
let currentPage = 1; let currentPage = 1;
const pageSize = 2000; // 100 const pageSize = 1000;
let allEvents = [];
// //
const { tableData: firstBatch, totalItems } = await apiInstance.getEvents(token, pageSize, currentPage); const { tableData: firstBatch, totalItems } = await apiInstance.getEvents(token, pageSize, currentPage);
allEvents = [...firstBatch]; allEvents.value = [...firstBatch]; // 使 allEvents.value
// //
const totalPages = Math.ceil(totalItems / pageSize); const totalPages = Math.ceil(totalItems / pageSize);
@ -191,42 +297,46 @@ const fetchAndProcessEvents = async (token) => {
while (currentPage < totalPages) { while (currentPage < totalPages) {
currentPage++; currentPage++;
const { tableData: nextBatch } = await apiInstance.getEvents(token, pageSize, currentPage); const { tableData: nextBatch } = await apiInstance.getEvents(token, pageSize, currentPage);
allEvents = [...allEvents, ...nextBatch]; allEvents.value = [...allEvents.value, ...nextBatch]; // 使 allEvents.value
//
processEventData(allEvents);
} }
// //
processEventData(allEvents); processEventData(allEvents.value); //
processWeekData(); //
processMonthData(); // 30
} catch (error) { } catch (error) {
console.error("Error fetching events:", error); console.error("Error fetching events:", error);
} }
}; };
// //
const processEventData = (events) => { const processEventData = (events) => {
//
hourlyCounts.value = Array(24).fill(0); hourlyCounts.value = Array(24).fill(0);
dailyCounts.value = Array(7).fill(0);
const today = new Date(); const today = new Date();
const startOfToday = new Date(today.getFullYear(), today.getMonth(), today.getDate()); const startOfToday = new Date(today.getFullYear(), today.getMonth(), today.getDate());
const endOfToday = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1); const endOfToday = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1);
// events.forEach((event) => {
events.forEach(event => {
const endedAt = event.ended_at; const endedAt = event.ended_at;
if (endedAt) { if (endedAt) {
const endedDate = new Date(endedAt); const endedDate = new Date(endedAt);
//
if (endedDate >= startOfToday && endedDate < endOfToday) { if (endedDate >= startOfToday && endedDate < endOfToday) {
const hour = endedDate.getHours(); const hour = endedDate.getHours();
hourlyCounts.value[hour] += 1; hourlyCounts.value[hour] += 1;
} }
}
});
// 7 updateCharts();
};
//
const processWeekData = () => {
dailyCounts.value = Array(7).fill(0);
allEvents.value.forEach((event) => {
const endedAt = event.ended_at;
if (endedAt) {
const endedDay = dayjs(endedAt).format('YYYY-MM-DD'); const endedDay = dayjs(endedAt).format('YYYY-MM-DD');
weekDates.value.forEach((date, index) => { weekDates.value.forEach((date, index) => {
if (endedDay === date) { if (endedDay === date) {
@ -236,72 +346,145 @@ const processEventData = (events) => {
} }
}); });
// updateWeekChart();
updateCharts();
}; };
// // 30
const processMonthData = () => {
monthlyCounts.value = Array(30).fill(0);
allEvents.value.forEach((event) => {
const endedAt = event.ended_at;
if (endedAt) {
const endedDay = dayjs(endedAt).format('YYYY-MM-DD');
monthDates.value.forEach((date, index) => {
if (endedDay === date) {
monthlyCounts.value[index] += 1;
}
});
}
});
updateMonthChart();
};
//
const updateCharts = () => { const updateCharts = () => {
if (chartInstanceToday.value) { if (chartInstanceToday.value) {
chartInstanceToday.value.setOption({ chartInstanceToday.value.setOption({
series: [{ data: hourlyCounts.value }] series: [{ data: hourlyCounts.value }],
});
}
if (chartInstanceWeek.value) {
chartInstanceWeek.value.setOption({
series: [{ data: dailyCounts.value }]
}); });
} }
}; };
//
const updateWeekChart = () => {
if (chartInstanceWeek.value) {
chartInstanceWeek.value.setOption({
series: [{ data: dailyCounts.value }],
});
}
};
// 30
const updateMonthChart = () => {
if (chartInstanceMonth.value) {
chartInstanceMonth.value.setOption({
series: [{ data: monthlyCounts.value }],
});
}
};
//
const handleClick = async (tab) => {
if (tab.props.name === 'first') {
if (!isTodayChartInitialized) {
await nextTick();
initTodayChart();
isTodayChartInitialized = true;
}
delayInstanceToday();
} else if (tab.props.name === 'second') {
await processWeekData();
if (!isWeekChartInitialized) {
await nextTick();
initWeekChart();
isWeekChartInitialized = true;
}
delayInstanceWeek();
} else if (tab.props.name === 'third') {
await processMonthData();
if (!isMonthChartInitialized) {
await nextTick();
initMonthChart();
isMonthChartInitialized = true;
}
delayInstanceMonth();
}
};
//
const delayInstanceToday = debounce(() => {
if (chartInstanceToday.value) {
chartInstanceToday.value.resize();
updateCharts();
}
}, 300);
const delayInstanceWeek = debounce(() => {
if (chartInstanceWeek.value) {
chartInstanceWeek.value.resize();
updateWeekChart();
}
}, 300);
const delayInstanceMonth = debounce(() => {
if (chartInstanceMonth.value) {
chartInstanceMonth.value.resize();
updateMonthChart();
}
}, 300);
// //
onMounted(async () => { onMounted(async () => {
calculateWeekDates(); // 7 calculateWeekDates(); // 7
calculateMonthDates(); // 30
const token = localStorage.getItem('alertToken'); const token = localStorage.getItem('alertToken');
await fetchAndProcessEvents(token); await fetchAndProcessEvents(token); //
//
initTodayChart(); initTodayChart();
initWeekChart();
}); });
// hourlyCounts //
watch(hourlyCounts, () => { watch(hourlyCounts, () => {
if (chartInstanceToday.value) { if (chartInstanceToday.value) {
chartInstanceToday.value.setOption({ chartInstanceToday.value.setOption({
series: [{ series: [{ data: hourlyCounts.value }],
data: hourlyCounts.value
}]
}); });
} }
}); });
// dailyCounts
watch(dailyCounts, () => { watch(dailyCounts, () => {
if (chartInstanceWeek.value) { if (chartInstanceWeek.value) {
chartInstanceWeek.value.setOption({ chartInstanceWeek.value.setOption({
series: [{ series: [{ data: dailyCounts.value }],
data: dailyCounts.value
}]
}); });
} }
}); });
// tab watch(monthlyCounts, () => {
const handleClick = (tab) => { if (chartInstanceMonth.value) {
if (tab.name === 'first') { chartInstanceMonth.value.setOption({
initTodayChart(); // series: [{ data: monthlyCounts.value }],
} else if (tab.name === 'second') { });
initWeekChart(); //
} }
}; });
</script> </script>
<style scoped> <style scoped>
.alert-card { .alert-card {
background-color: #2a3f54; background-color: #304555;
color: #fff; color: #fff;
border-radius: 8px; border-radius: 8px;
padding: 0; padding: 0;
@ -314,20 +497,15 @@ const handleClick = (tab) => {
border-bottom: 1px solid #3a4b5c; border-bottom: 1px solid #3a4b5c;
} }
.alert-tabs { .chart-container {
margin-bottom: 0; height: 350px;
} }
::v-deep .el-tabs__item { ::v-deep .el-tabs__item {
color: #fff; color: #fff;
} }
.chart-container { ::v-deep .el-tabs__item.is-active {
width: 780px; color: #2ea0ec;
height: 420px;
}
.el-tabs__active-bar {
background-color: #409EFF;
} }
</style> </style>