告警趋势资源复用加载问题修复,新增30天告警分布
This commit is contained in:
parent
8184d3ca94
commit
93f9af1c99
|
@ -9,27 +9,42 @@
|
|||
<el-tab-pane label="本周" name="second">
|
||||
<div ref="weekLineChartDom" class="chart-container"></div> <!-- 本周图表 -->
|
||||
</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-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, watch } from 'vue';
|
||||
import { ref, onMounted, watch, nextTick } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
import { BoxApi } from '@/utils/boxApi.ts';
|
||||
import dayjs from 'dayjs';
|
||||
import { BoxApi } from '@/utils/boxApi.ts'; // 使用 BoxApi
|
||||
import { debounce } from 'lodash';
|
||||
|
||||
const activeName = ref('first');
|
||||
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 weekDates = ref([]); // 存储前7天的日期
|
||||
const chartInstanceToday = ref(null);
|
||||
const chartInstanceWeek = ref(null);
|
||||
const todayLineChartDom = 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天的日期
|
||||
const calculateWeekDates = () => {
|
||||
|
@ -37,6 +52,84 @@ const calculateWeekDates = () => {
|
|||
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 = () => {
|
||||
if (weekLineChartDom.value) {
|
||||
|
@ -49,13 +142,13 @@ const initWeekChart = () => {
|
|||
textStyle: {
|
||||
color: '#fff',
|
||||
fontSize: 18,
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross'
|
||||
type: 'cross',
|
||||
},
|
||||
formatter: function (params) {
|
||||
const { value } = params[0];
|
||||
|
@ -63,19 +156,19 @@ const initWeekChart = () => {
|
|||
},
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
fontSize: 12
|
||||
}
|
||||
fontSize: 12,
|
||||
},
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: true,
|
||||
data: weekDates.value.length > 0 ? weekDates.value : Array.from({ length: 7 }, (_, i) => `日期 ${i+1}`),
|
||||
data: weekDates.value.length > 0 ? weekDates.value : Array.from({ length: 7 }, (_, i) => `日期 ${i + 1}`),
|
||||
axisLabel: {
|
||||
rotate: 0,
|
||||
interval: 0,
|
||||
color: '#fff',
|
||||
fontSize: 12
|
||||
}
|
||||
fontSize: 12,
|
||||
},
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
|
@ -83,8 +176,8 @@ const initWeekChart = () => {
|
|||
minInterval: 1,
|
||||
axisLabel: {
|
||||
color: '#fff',
|
||||
fontSize: 12
|
||||
}
|
||||
fontSize: 12,
|
||||
},
|
||||
},
|
||||
series: [
|
||||
{
|
||||
|
@ -97,35 +190,42 @@ const initWeekChart = () => {
|
|||
position: 'top',
|
||||
textStyle: {
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
// 初始化今日图表
|
||||
const initTodayChart = () => {
|
||||
if (todayLineChartDom.value) {
|
||||
chartInstanceToday.value = echarts.init(todayLineChartDom.value);
|
||||
// 初始化最近30天图表
|
||||
const initMonthChart = () => {
|
||||
if (monthLineChartDom.value) {
|
||||
chartInstanceMonth.value = echarts.init(monthLineChartDom.value);
|
||||
|
||||
const option = {
|
||||
title: {
|
||||
text: '今日告警数量趋势',
|
||||
text: '最近30天告警数量趋势',
|
||||
top: '3%',
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'cross'
|
||||
type: 'cross',
|
||||
},
|
||||
formatter: function (params) {
|
||||
const { value, name } = params[0];
|
||||
|
@ -133,17 +233,17 @@ const initTodayChart = () => {
|
|||
},
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
fontSize: 12
|
||||
}
|
||||
fontSize: 12,
|
||||
},
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: Array.from({ length: 24 }, (_, i) => `${i.toString().padStart(2, '0')}:00`),
|
||||
data: monthDates.value,
|
||||
axisLabel: {
|
||||
rotate: 0,
|
||||
color: '#fff',
|
||||
}
|
||||
},
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
|
@ -151,38 +251,44 @@ const initTodayChart = () => {
|
|||
minInterval: 1,
|
||||
axisLabel: {
|
||||
color: '#fff',
|
||||
}
|
||||
},
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '告警数量',
|
||||
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,
|
||||
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);
|
||||
chartInstanceMonth.value.setOption(option);
|
||||
}
|
||||
};
|
||||
|
||||
// 分批获取并处理告警数据
|
||||
// 获取并处理告警数据
|
||||
const fetchAndProcessEvents = async (token) => {
|
||||
try {
|
||||
let currentPage = 1;
|
||||
const pageSize = 2000; // 每次加载100条数据
|
||||
let allEvents = [];
|
||||
const pageSize = 1000;
|
||||
|
||||
// 第一次请求,获取告警总数
|
||||
const { tableData: firstBatch, totalItems } = await apiInstance.getEvents(token, pageSize, currentPage);
|
||||
allEvents = [...firstBatch];
|
||||
allEvents.value = [...firstBatch]; // 使用 allEvents.value
|
||||
|
||||
// 根据告警总数逐页加载所有数据
|
||||
const totalPages = Math.ceil(totalItems / pageSize);
|
||||
|
@ -191,42 +297,46 @@ const fetchAndProcessEvents = async (token) => {
|
|||
while (currentPage < totalPages) {
|
||||
currentPage++;
|
||||
const { tableData: nextBatch } = await apiInstance.getEvents(token, pageSize, currentPage);
|
||||
allEvents = [...allEvents, ...nextBatch];
|
||||
|
||||
// 每次加载数据后逐步更新图表
|
||||
processEventData(allEvents);
|
||||
allEvents.value = [...allEvents.value, ...nextBatch]; // 使用 allEvents.value
|
||||
}
|
||||
|
||||
// 最终处理全部数据
|
||||
processEventData(allEvents);
|
||||
// 处理数据
|
||||
processEventData(allEvents.value); // 今日告警数据
|
||||
processWeekData(); // 本周告警数据
|
||||
processMonthData(); // 最近30天告警数据
|
||||
} catch (error) {
|
||||
console.error("Error fetching events:", error);
|
||||
}
|
||||
};
|
||||
|
||||
// 处理告警数据并更新图表
|
||||
// 处理今日告警数据并更新图表
|
||||
const processEventData = (events) => {
|
||||
// 重置计数器
|
||||
hourlyCounts.value = Array(24).fill(0);
|
||||
dailyCounts.value = Array(7).fill(0);
|
||||
|
||||
const today = new Date();
|
||||
const startOfToday = new Date(today.getFullYear(), today.getMonth(), today.getDate());
|
||||
const endOfToday = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1);
|
||||
|
||||
// 统计数据
|
||||
events.forEach(event => {
|
||||
events.forEach((event) => {
|
||||
const endedAt = event.ended_at;
|
||||
if (endedAt) {
|
||||
const endedDate = new Date(endedAt);
|
||||
|
||||
// 统计今日每小时的告警数量
|
||||
if (endedDate >= startOfToday && endedDate < endOfToday) {
|
||||
const hour = endedDate.getHours();
|
||||
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');
|
||||
weekDates.value.forEach((date, index) => {
|
||||
if (endedDay === date) {
|
||||
|
@ -236,72 +346,145 @@ const processEventData = (events) => {
|
|||
}
|
||||
});
|
||||
|
||||
// 更新图表
|
||||
updateCharts();
|
||||
updateWeekChart();
|
||||
};
|
||||
|
||||
// 更新图表
|
||||
// 处理最近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 = () => {
|
||||
if (chartInstanceToday.value) {
|
||||
chartInstanceToday.value.setOption({
|
||||
series: [{ data: hourlyCounts.value }]
|
||||
});
|
||||
}
|
||||
|
||||
if (chartInstanceWeek.value) {
|
||||
chartInstanceWeek.value.setOption({
|
||||
series: [{ data: dailyCounts.value }]
|
||||
series: [{ data: hourlyCounts.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 () => {
|
||||
calculateWeekDates(); // 计算前7天日期
|
||||
|
||||
calculateMonthDates(); // 计算前30天日期
|
||||
const token = localStorage.getItem('alertToken');
|
||||
await fetchAndProcessEvents(token);
|
||||
await fetchAndProcessEvents(token); // 加载数据
|
||||
|
||||
// 初始化图表
|
||||
initTodayChart();
|
||||
initWeekChart();
|
||||
});
|
||||
|
||||
// 监听hourlyCounts,当其变化时更新今日图表
|
||||
// 监听数据变化,更新对应的图表
|
||||
watch(hourlyCounts, () => {
|
||||
if (chartInstanceToday.value) {
|
||||
chartInstanceToday.value.setOption({
|
||||
series: [{
|
||||
data: hourlyCounts.value
|
||||
}]
|
||||
series: [{ data: hourlyCounts.value }],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 监听dailyCounts,当其变化时更新本周图表
|
||||
watch(dailyCounts, () => {
|
||||
if (chartInstanceWeek.value) {
|
||||
chartInstanceWeek.value.setOption({
|
||||
series: [{
|
||||
data: dailyCounts.value
|
||||
}]
|
||||
series: [{ data: dailyCounts.value }],
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// tab点击处理函数
|
||||
const handleClick = (tab) => {
|
||||
if (tab.name === 'first') {
|
||||
initTodayChart(); // 切换到今日,重新加载今日图表
|
||||
} else if (tab.name === 'second') {
|
||||
initWeekChart(); // 切换到本周,重新加载本周图表
|
||||
watch(monthlyCounts, () => {
|
||||
if (chartInstanceMonth.value) {
|
||||
chartInstanceMonth.value.setOption({
|
||||
series: [{ data: monthlyCounts.value }],
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.alert-card {
|
||||
background-color: #2a3f54;
|
||||
background-color: #304555;
|
||||
color: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 0;
|
||||
|
@ -314,20 +497,15 @@ const handleClick = (tab) => {
|
|||
border-bottom: 1px solid #3a4b5c;
|
||||
}
|
||||
|
||||
.alert-tabs {
|
||||
margin-bottom: 0;
|
||||
.chart-container {
|
||||
height: 350px;
|
||||
}
|
||||
|
||||
::v-deep .el-tabs__item {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
width: 780px;
|
||||
height: 420px;
|
||||
}
|
||||
|
||||
.el-tabs__active-bar {
|
||||
background-color: #409EFF;
|
||||
::v-deep .el-tabs__item.is-active {
|
||||
color: #2ea0ec;
|
||||
}
|
||||
</style>
|
||||
|
|
Loading…
Reference in New Issue