404 lines
9.0 KiB
Vue
404 lines
9.0 KiB
Vue
<template>
|
||
<div class="alarm-statistics">
|
||
<div class="time-filter">
|
||
<div
|
||
v-for="period in periods"
|
||
:key="period.value"
|
||
:class="['filter-item', { active: currentPeriod === period.value }]"
|
||
@click="handleManualSelect(period.value)"
|
||
>
|
||
{{ period.label }}
|
||
</div>
|
||
</div>
|
||
|
||
<div class="chart-container" ref="chartRef"></div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, onMounted, onUnmounted, watch, computed } from "vue";
|
||
import * as echarts from "echarts";
|
||
import { homeApi } from '../api/index';
|
||
import emptyList from '../assets/img/empty_list.png';
|
||
|
||
const chartRef = ref(null);
|
||
let chart = null;
|
||
let autoSwitchTimer = null;
|
||
let checkInterval = null;
|
||
const isLoading = ref(false);
|
||
const isManualMode = ref(false); // 是否处于手动选择模式
|
||
|
||
// 告警事件类型映射
|
||
const eventTypeMap = {
|
||
'0': '读表告警',
|
||
'1': '高温感知报警',
|
||
'2': '吸烟报警',
|
||
'3': '长时间滞留报警',
|
||
'4': '空气质量报警',
|
||
'5': '急停按下',
|
||
'6': '语音未接通',
|
||
'7': '日常巡检'
|
||
};
|
||
|
||
// 存储API返回的数据
|
||
const apiData = ref({
|
||
'1': [], // 天
|
||
'7': [], // 周
|
||
'30': [], // 月
|
||
});
|
||
|
||
// 判断当前时间段是否有数据
|
||
const hasData = computed(() => {
|
||
return apiData.value[currentPeriod.value] && apiData.value[currentPeriod.value].length > 0;
|
||
});
|
||
|
||
// 转换API数据为图表格式
|
||
const transformData = (data) => {
|
||
const xAxisData = [];
|
||
const seriesData = [];
|
||
|
||
if (!data || data.length === 0) {
|
||
return { xAxis: [], series: [] };
|
||
}
|
||
|
||
// 按事件类型分组并排序
|
||
data.forEach(item => {
|
||
const eventTypeName = eventTypeMap[item.eventType] || `未知类型(${item.eventType})`;
|
||
xAxisData.push(eventTypeName);
|
||
seriesData.push(item.count);
|
||
});
|
||
|
||
return {
|
||
xAxis: xAxisData,
|
||
series: seriesData
|
||
};
|
||
};
|
||
|
||
const periods = [
|
||
{ label: "天", value: "1" },
|
||
{ label: "周", value: "7" },
|
||
{ label: "月", value: "30" },
|
||
];
|
||
|
||
const currentPeriod = ref("1");
|
||
|
||
// 获取指定时间段的告警统计数据
|
||
const fetchAlarmStatistics = async (day) => {
|
||
console.log(`获取${day}天的数据`);
|
||
isLoading.value = true;
|
||
|
||
try {
|
||
const res = await homeApi.getAlarmStatistics(day);
|
||
if (res.code === 200) {
|
||
apiData.value[day] = res.data || [];
|
||
console.log(`获取${day}天告警统计数据成功:`, res.data);
|
||
} else {
|
||
console.error(`获取${day}天告警统计数据失败:`, res);
|
||
apiData.value[day] = [];
|
||
}
|
||
} catch (err) {
|
||
console.error(`获取${day}天告警统计数据错误:`, err);
|
||
apiData.value[day] = [];
|
||
} finally {
|
||
isLoading.value = false;
|
||
|
||
// 确保当前还是这个时间段才更新图表
|
||
if (currentPeriod.value === day) {
|
||
updateChart();
|
||
}
|
||
}
|
||
};
|
||
|
||
// 更新图表数据
|
||
const updateChart = () => {
|
||
if (!chartRef.value || !chart) return;
|
||
|
||
console.log(`更新图表数据,当前时间段: ${currentPeriod.value}`);
|
||
|
||
// 获取当前时间段的数据
|
||
const chartData = transformData(apiData.value[currentPeriod.value]);
|
||
|
||
// 基础配置,无论是否有数据都会显示
|
||
const option = {
|
||
grid: {
|
||
top: "10%",
|
||
left: "5%",
|
||
right: "4%",
|
||
bottom: "15%",
|
||
containLabel: true,
|
||
},
|
||
xAxis: {
|
||
type: "category",
|
||
// 如果没有数据,使用空数组但仍显示坐标轴
|
||
data: chartData.xAxis.length > 0 ? chartData.xAxis : ['暂无数据'],
|
||
axisLine: {
|
||
show: true,
|
||
lineStyle: {
|
||
color: "rgba(185, 232, 255, 0.1)",
|
||
},
|
||
},
|
||
axisTick: {
|
||
show: false,
|
||
},
|
||
axisLabel: {
|
||
color: "#B9E8FF",
|
||
fontSize: 12,
|
||
interval: 0,
|
||
rotate: 45,
|
||
},
|
||
},
|
||
yAxis: {
|
||
type: "value",
|
||
name: "数量: 次",
|
||
nameTextStyle: {
|
||
color: "#B9E8FF",
|
||
fontSize: 12,
|
||
padding: [0, 30, 0, 0],
|
||
},
|
||
splitLine: {
|
||
show: true,
|
||
lineStyle: {
|
||
color: "rgba(185, 232, 255, 0.1)",
|
||
type: "dashed",
|
||
},
|
||
},
|
||
axisLine: {
|
||
show: false,
|
||
},
|
||
axisTick: {
|
||
show: false,
|
||
},
|
||
axisLabel: {
|
||
color: "#B9E8FF",
|
||
fontSize: 12,
|
||
},
|
||
},
|
||
series: [
|
||
{
|
||
// 当没有数据时,显示一个空数据点
|
||
data: chartData.series.length > 0 ? chartData.series : [0],
|
||
type: "line",
|
||
smooth: true,
|
||
symbol: "circle",
|
||
symbolSize: 8,
|
||
itemStyle: {
|
||
color: "#FF8A00",
|
||
},
|
||
lineStyle: {
|
||
color: {
|
||
type: "linear",
|
||
x: 0,
|
||
y: 0,
|
||
x2: 0,
|
||
y2: 1,
|
||
colorStops: [
|
||
{
|
||
offset: 0,
|
||
color: "#FF8A00",
|
||
},
|
||
{
|
||
offset: 1,
|
||
color: "rgba(255, 138, 0, 0)",
|
||
},
|
||
],
|
||
},
|
||
width: 3,
|
||
},
|
||
areaStyle: {
|
||
color: {
|
||
type: "linear",
|
||
x: 0,
|
||
y: 0,
|
||
x2: 0,
|
||
y2: 1,
|
||
colorStops: [
|
||
{
|
||
offset: 0,
|
||
color: "rgba(255, 138, 0, 0.3)",
|
||
},
|
||
{
|
||
offset: 1,
|
||
color: "rgba(255, 138, 0, 0)",
|
||
},
|
||
],
|
||
},
|
||
},
|
||
label: {
|
||
show: true,
|
||
position: 'top',
|
||
color: '#FF8A00',
|
||
fontSize: 14,
|
||
fontWeight: 'bold',
|
||
},
|
||
},
|
||
],
|
||
};
|
||
|
||
console.log('设置图表选项');
|
||
chart.setOption(option, true); // 使用true参数完全覆盖之前的配置
|
||
};
|
||
|
||
// 初始化图表
|
||
const initChart = () => {
|
||
if (!chartRef.value) return;
|
||
|
||
if (chart) {
|
||
chart.dispose();
|
||
}
|
||
|
||
chart = echarts.init(chartRef.value);
|
||
updateChart(); // 初始更新图表
|
||
};
|
||
|
||
// 开始自动轮播
|
||
const startAutoSwitch = () => {
|
||
stopAutoSwitch(); // 先停止现有的定时器
|
||
|
||
autoSwitchTimer = setInterval(async () => {
|
||
// 如果处于手动模式,不自动切换
|
||
if (isManualMode.value) return;
|
||
|
||
const currentIndex = periods.findIndex(p => p.value === currentPeriod.value);
|
||
const nextIndex = (currentIndex + 1) % periods.length;
|
||
const nextPeriod = periods[nextIndex].value;
|
||
|
||
console.log(`自动切换到: ${periods[nextIndex].label}(${nextPeriod})`);
|
||
|
||
// 先切换时间段
|
||
currentPeriod.value = nextPeriod;
|
||
|
||
// 然后获取数据
|
||
await fetchAlarmStatistics(nextPeriod);
|
||
}, 10000); // 每10秒切换一次
|
||
};
|
||
|
||
// 停止自动轮播
|
||
const stopAutoSwitch = () => {
|
||
if (autoSwitchTimer) {
|
||
clearInterval(autoSwitchTimer);
|
||
autoSwitchTimer = null;
|
||
}
|
||
};
|
||
|
||
// 处理手动选择
|
||
const handleManualSelect = async (period) => {
|
||
console.log(`手动选择时间段: ${period}`);
|
||
|
||
// 进入手动模式
|
||
isManualMode.value = true;
|
||
|
||
// 如果点击的是当前已选择的时间段,不做任何操作
|
||
if (currentPeriod.value === period) return;
|
||
|
||
// 更新当前选中时间段
|
||
currentPeriod.value = period;
|
||
|
||
// 获取该时间段的数据
|
||
await fetchAlarmStatistics(period);
|
||
|
||
// 5秒后恢复自动切换
|
||
setTimeout(() => {
|
||
console.log('恢复自动模式');
|
||
isManualMode.value = false;
|
||
}, 30000); // 30秒后恢复自动模式
|
||
};
|
||
|
||
// 监听窗口大小变化
|
||
const handleResize = () => {
|
||
chart && chart.resize();
|
||
};
|
||
|
||
// 监听当前选中的时间段变化
|
||
watch(currentPeriod, (newPeriod) => {
|
||
console.log(`当前选中时间段变为: ${newPeriod}`);
|
||
});
|
||
|
||
onMounted(async () => {
|
||
console.log('组件挂载');
|
||
|
||
// 初始化时获取默认数据(1天)
|
||
await fetchAlarmStatistics('1');
|
||
|
||
// 然后初始化其他时间段数据
|
||
Promise.all([
|
||
fetchAlarmStatistics('7'),
|
||
fetchAlarmStatistics('30')
|
||
]);
|
||
|
||
initChart();
|
||
window.addEventListener("resize", handleResize);
|
||
|
||
// 启动自动轮播
|
||
console.log('开始自动轮播');
|
||
startAutoSwitch();
|
||
|
||
// 定期检查轮播是否还在进行
|
||
checkInterval = setInterval(() => {
|
||
if (!autoSwitchTimer) {
|
||
console.log('检测到轮播已停止,重新启动');
|
||
startAutoSwitch();
|
||
}
|
||
}, 20000); // 每20秒检查一次
|
||
});
|
||
|
||
onUnmounted(() => {
|
||
// 清理定时器
|
||
stopAutoSwitch();
|
||
if (checkInterval) {
|
||
clearInterval(checkInterval);
|
||
checkInterval = null;
|
||
}
|
||
|
||
// 销毁图表
|
||
if (chart) {
|
||
chart.dispose();
|
||
chart = null;
|
||
}
|
||
|
||
window.removeEventListener("resize", handleResize);
|
||
console.log('组件卸载,停止自动轮播');
|
||
});
|
||
</script>
|
||
|
||
<style scoped>
|
||
.alarm-statistics {
|
||
width: 100%;
|
||
height: 100%;
|
||
position: relative;
|
||
}
|
||
|
||
.chart-container {
|
||
width: 100%;
|
||
height: 100%;
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
z-index: 1;
|
||
}
|
||
|
||
.time-filter {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
gap: 5px;
|
||
padding: 0 10px;
|
||
position: relative;
|
||
z-index: 3;
|
||
}
|
||
|
||
.filter-item {
|
||
color: #b9e8ff;
|
||
cursor: pointer;
|
||
padding: 2px 10px;
|
||
border-radius: 2px;
|
||
transition: all 0.3s;
|
||
}
|
||
|
||
.filter-item.active {
|
||
padding: 2px 10px;
|
||
background: url("../assets/img/alarm_tri.png") no-repeat;
|
||
background-size: 100% 100%;
|
||
color: #00ffff;
|
||
}
|
||
|
||
</style>
|