-
-
-
+
-
@@ -29,22 +27,15 @@
import { getAlarmDet } from '@/api/base/equipment';
import * as echarts from 'echarts';
import SmallTitle from './SmallTitle';
-// import { ElEmpty } from 'element-plus';
-// 图表颜色配置(统一风格)
const CHART_CONFIG = {
- // 柱状图颜色
barColor: '#288AFF',
- // 饼图颜色(渐变色系,避免刺眼)
pieColors: [
'#288AFF', '#4096FF', '#69B1FF', '#91CFFF', '#B8E0FF',
'#E0F2FF', '#1890FF', '#096DD9', '#0050B3', '#003A8C'
],
- // 字体颜色
fontColor: '#333',
- // 浅色字体
lightFontColor: '#666',
- // 边框圆角
borderRadius: 4
};
@@ -53,7 +44,7 @@ export default {
data() {
return {
visible: false,
- hasData: true, // 是否有数据标识
+ hasData: false,
listQuery: {
pageNo: 1,
pageSize: 100,
@@ -82,40 +73,58 @@ export default {
color: 'primary'
}
],
- activeLabel: 'duration', // 默认激活时长tab
+ activeLabel: 'duration', // 默认选中「报警时长」
dataForm: {
equipmentId: undefined,
equipmentName: undefined,
lineId: undefined
},
- // 存储图表实例,用于销毁
chartInstances: {
bar: null,
pie: null
- }
+ },
+ isDomReady: false,
+ originData: null // 存储原始数据
};
},
mounted() {
- // 初始化默认日期(当天)
- this.initDefaultDate();
+ this.$nextTick(() => {
+ this.isDomReady = true;
+ if (this.listQuery.equipmentId) {
+ this.getDataList();
+ }
+ });
+ },
+ watch: {
+ // Tab 切换时自动刷新图表(无需额外操作,依赖 handleTabClick 触发查询)
+ activeLabel() {
+ if (this.isDomReady && this.originData) {
+ this.$nextTick(() => {
+ this.renderBothCharts(); // 切换 Tab 后重新渲染两个图表
+ });
+ }
+ }
},
methods: {
- // 初始化默认日期(当天零点到23:59:59)
initDefaultDate() {
const today = new Date();
- const start = new Date(today.setHours(0, 0, 0, 0)).getTime();
- const end = new Date(today.setHours(23, 59, 59, 999)).getTime();
+ const start = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 0, 0, 0, 0).getTime();
+ const end = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 23, 59, 59, 0).getTime();
+
this.formConfig[0].defaultSelect = [start, end];
this.listQuery.startTime = start;
this.listQuery.endTime = end;
+
+ if (this.$refs.searchBarForm) {
+ this.$refs.searchBarForm.form.timeVal = [start, end];
+ }
},
- // 切换Tab时加载对应图表
handleTabClick() {
+ // 切换 Tab 时重新查询数据(或直接复用已有数据渲染)
this.getDataList();
},
- // 搜索按钮点击事件
buttonClick(val) {
switch (val.btnName) {
case 'search':
@@ -123,43 +132,34 @@ export default {
this.listQuery.endTime = val.timeVal?.[1];
this.getDataList();
break;
- case 'link':
- this.disabled = true;
- this.connectSSEBatch(this.listQuery.equipmentIds);
- break;
default:
}
},
- // 获取数据并渲染对应图表
async getDataList() {
try {
- // 校验必要参数
if (!this.listQuery.equipmentId) {
console.warn('设备ID不能为空');
this.hasData = false;
return;
}
- // 构造请求参数(移除硬编码,使用动态参数)
const queryParams = {
equipmentId: this.listQuery.equipmentId,
startTime: this.listQuery.startTime,
endTime: this.listQuery.endTime,
};
- const res = await getAlarmDet(queryParams)
+ const res = await getAlarmDet(queryParams);
const originData = res.data || [];
-
+ this.originData = originData;
this.hasData = originData.length > 0;
- if (this.hasData) {
- // 根据当前Tab渲染对应图表
- this.activeLabel === 'duration'
- ? this.renderBarChart(originData)
- : this.renderPieChart(originData);
+ if (this.hasData && this.isDomReady) {
+ this.$nextTick(() => {
+ this.renderBothCharts(); // 数据查询成功后,同时渲染两个图表
+ });
} else {
- // 无数据时销毁已有图表
this.destroyAllCharts();
}
} catch (error) {
@@ -169,213 +169,251 @@ export default {
}
},
- // 渲染报警时长-柱状图
- renderBarChart(originData) {
- // 先销毁原有图表
- this.destroyChart('chart');
-
- // 按报警时长降序排序
- const sortedData = [...originData].sort((a, b) => b.alarmDuration - a.alarmDuration);
-
- // 处理图表数据
- const xData = sortedData.map(item => this.truncateText(item.alarmContent, 8)); // 文本截断(最多8字)
- const seriesData = sortedData.map(item => item.alarmDuration);
-
- const chartDom = document.getElementById('barChart');
- this.chartInstances.bar = echarts.init(chartDom);
-
- const option = {
- tooltip: {
- trigger: 'axis',
- axisPointer: { type: 'shadow' },
- padding: 10,
- textStyle: { fontSize: 11 },
- // 自定义提示框内容
- formatter: (params) => {
- const index = params[0].dataIndex;
- const item = sortedData[index];
- return `
-
-
${item.alarmContent}
-
报警时长:${item.alarmDuration}
-
占比:${item.alarmDurationRatio.toFixed(2)}%
-
- `;
- }
- },
- grid: {
- left: '5%',
- right: '5%',
- bottom: '18%', // 底部留足空间显示x轴标签
- top: '10%',
- containLabel: true
- },
- xAxis: [
- {
- type: 'category',
- data: xData,
- axisTick: { alignWithLabel: true },
- axisLabel: {
- interval: 0,
- // rotate: 35, // 标签旋转
- fontSize: 12,
- color: CHART_CONFIG.lightFontColor
- },
- axisLine: { lineStyle: { color: '#e8e8e8' } }
- }
- ],
- yAxis: [
- {
- type: 'value',
- axisLabel: {
- fontSize: 11,
- color: CHART_CONFIG.lightFontColor,
- // formatter: '{value}s' // 显示单位
- },
- axisLine: { lineStyle: { color: '#e8e8e8' } },
- splitLine: { lineStyle: { color: '#f5f5f5' } },
- // 扩展y轴最大值,避免label超出
- max: (value) => value.max * 1.2
- }
- ],
- series: [
- {
- name: '报警时长',
- type: 'bar',
- itemStyle: {
- color: CHART_CONFIG.barColor,
- borderRadius: [CHART_CONFIG.borderRadius, CHART_CONFIG.borderRadius, 0, 0],
- shadowBlur: 3,
- shadowColor: 'rgba(40, 138, 255, 0.2)',
- shadowOffsetY: 2
- },
- barWidth: '16', // 柱子宽度(百分比适配)
- data: seriesData,
- label: {
- show: true,
- position: 'top',
- distance: 6,
- fontSize: 11,
- color: CHART_CONFIG.fontColor,
- formatter: (params) => `${params.value}` // 显示单位
- }
- }
- ]
- };
-
- this.chartInstances.bar.setOption(option);
- // 监听窗口 resize
- this.addResizeListener('bar');
+ // 核心方法:同时渲染柱状图和饼图(根据当前 Tab 类型)
+ renderBothCharts() {
+ if (this.activeLabel === 'duration') {
+ // 报警时长:柱状图(时长排序)+ 饼图(时长占比)
+ this.renderBarChart('duration');
+ this.renderPieChart('duration');
+ } else {
+ // 报警次数:柱状图(次数排序)+ 饼图(次数占比)
+ this.renderBarChart('times');
+ this.renderPieChart('times');
+ }
},
- // 渲染报警次数-饼图
- renderPieChart(originData) {
- // 先销毁原有图表
- this.destroyChart('pie');
+ // 渲染柱状图(支持两种数据类型)
+ renderBarChart(type) {
+ this.destroyChart('bar');
+ const chartDom = document.getElementById('barChart');
+ if (!chartDom || !this.originData.length) return;
- // 按报警次数降序排序
- const sortedData = [...originData].sort((a, b) => b.alarmCount - a.alarmCount);
+ // 根据类型排序和提取数据
+ let sortedData, xData, seriesData, yAxisName;
+ if (type === 'duration') {
+ // 报警时长:按时长降序
+ sortedData = [...this.originData].sort((a, b) => b.alarmDuration - a.alarmDuration);
+ seriesData = sortedData.map(item => item.alarmDuration);
+ yAxisName = '报警时长';
+ } else {
+ // 报警次数:按次数降序
+ sortedData = [...this.originData].sort((a, b) => b.alarmCount - a.alarmCount);
+ seriesData = sortedData.map(item => item.alarmCount);
+ yAxisName = '报警次数';
+ }
- // 处理图表数据(合并占比过小的项为"其他")
- const pieData = this.handlePieData(sortedData);
+ xData = sortedData.map(item => this.truncateText(item.alarmContent, 8));
- const chartDom = document.getElementById('pieChart');
- this.chartInstances.pie = echarts.init(chartDom);
-
- const option = {
- tooltip: {
- trigger: 'item',
- padding: 10,
- textStyle: { fontSize: 11 },
- formatter: (params) => {
- return `
-
-
${params.name}
-
报警次数:${params.value} 次
-
占比:${params.percent.toFixed(2)}%
-
- `;
- }
- },
- series: [
- {
- name: '报警次数',
- type: 'pie',
- radius: ['50%', '70%'], // 环形饼图
- center: ['50%', '50%'], // 饼图居中显示
- color: CHART_CONFIG.pieColors,
- // 关键:添加外部标签和指示线
- label: {
- show: true, // 显示标签
- position: 'outside', // 标签位置:饼图外部
- distance: 15, // 标签与饼图的距离
- fontSize: 11,
- color: CHART_CONFIG.lightFontColor,
- formatter: (params) => {
- // 标签内容:报警内容(截断)+ 次数 + 占比
- const truncatedName = this.truncateText(params.name, 8); // 最多8字
- return `${truncatedName}(${params.value}次, ${params.percent.toFixed(1)}%)`;
+ try {
+ this.chartInstances.bar = echarts.init(chartDom);
+ const option = {
+ title: {
+ text: `${yAxisName}统计(柱状图)`,
+ left: 'center',
+ textStyle: { fontSize: 14, color: CHART_CONFIG.fontColor }
+ },
+ tooltip: {
+ trigger: 'axis',
+ axisPointer: { type: 'shadow' },
+ padding: 10,
+ textStyle: { fontSize: 11 },
+ formatter: (params) => {
+ const index = params[0].dataIndex;
+ const item = sortedData[index];
+ return `
+
+
${item.alarmContent}
+
${yAxisName}:${type === 'duration' ? item.alarmDuration : item.alarmCount}
+
占比:${type === 'duration' ? item.alarmDurationRatio.toFixed(2) : item.alarmCountRatio.toFixed(2)}%
+
+ `;
+ }
+ },
+ grid: {
+ left: '5%',
+ right: '5%',
+ bottom: '18%',
+ top: '15%',
+ containLabel: true
+ },
+ xAxis: [
+ {
+ type: 'category',
+ data: xData,
+ axisTick: { alignWithLabel: true },
+ axisLabel: {
+ interval: 0,
+ fontSize: 12,
+ color: CHART_CONFIG.lightFontColor
},
- align: 'center',
- baseline: 'middle'
- },
- // 指示线配置
- labelLine: {
- show: true, // 显示指示线
- length: 15, // 第一段线长度(从饼图到转折点)
- length2: 20, // 第二段线长度(从转折点到标签)
- lineStyle: {
- color: '#ccc', // 指示线颜色
- width: 1, // 线宽
- type: 'solid' // 实线
+ axisLine: { lineStyle: { color: '#e8e8e8' } }
+ }
+ ],
+ yAxis: [
+ {
+ type: 'value',
+ name: yAxisName,
+ nameTextStyle: { fontSize: 11, color: CHART_CONFIG.lightFontColor },
+ axisLabel: {
+ fontSize: 11,
+ color: CHART_CONFIG.lightFontColor,
},
- smooth: 0.2 // 指示线轻微弯曲,更美观
- },
- data: pieData,
- // 高亮样式(hover时)
- emphasis: {
+ axisLine: { lineStyle: { color: '#e8e8e8' } },
+ splitLine: { lineStyle: { color: '#f5f5f5' } },
+ max: (value) => value.max * 1.2
+ }
+ ],
+ series: [
+ {
+ name: yAxisName,
+ type: 'bar',
itemStyle: {
- shadowBlur: 10,
- shadowColor: 'rgba(0, 0, 0, 0.1)'
+ color: CHART_CONFIG.barColor,
+ borderRadius: [CHART_CONFIG.borderRadius, CHART_CONFIG.borderRadius, 0, 0],
+ shadowBlur: 3,
+ shadowColor: 'rgba(40, 138, 255, 0.2)',
+ shadowOffsetY: 2
},
+ barWidth: '16',
+ data: seriesData,
label: {
- color: CHART_CONFIG.fontColor, // 高亮时标签颜色加深
- fontSize: 12, // 标签放大
- fontWeight: 500
+ show: true,
+ position: 'top',
+ distance: 6,
+ fontSize: 11,
+ color: CHART_CONFIG.fontColor,
+ formatter: (params) => `${params.value}`
+ }
+ }
+ ]
+ };
+
+ this.chartInstances.bar.setOption(option);
+ this.addResizeListener('bar');
+ } catch (error) {
+ console.error(`${yAxisName}柱状图初始化失败:`, error);
+ setTimeout(() => this.renderBarChart(type), 200);
+ }
+ },
+
+ // 渲染饼图(支持两种数据类型)
+ renderPieChart(type) {
+ this.destroyChart('pie');
+ const chartDom = document.getElementById('pieChart');
+ if (!chartDom || !this.originData.length) return;
+
+ // 根据类型处理饼图数据
+ let pieData, seriesName;
+ if (type === 'duration') {
+ // 报警时长:按时长占比处理
+ seriesName = '报警时长';
+ pieData = this.handlePieData(this.originData, 'alarmDuration', 'alarmDurationRatio');
+ } else {
+ // 报警次数:按次数占比处理
+ seriesName = '报警次数';
+ pieData = this.handlePieData(this.originData, 'alarmCount', 'alarmCountRatio');
+ }
+
+ try {
+ this.chartInstances.pie = echarts.init(chartDom);
+ const option = {
+ title: {
+ text: `${seriesName}统计(饼图)`,
+ left: 'center',
+ textStyle: { fontSize: 14, color: CHART_CONFIG.fontColor }
+ },
+ tooltip: {
+ trigger: 'item',
+ padding: 10,
+ textStyle: { fontSize: 11 },
+ formatter: (params) => {
+ return `
+
+
${params.name}
+
${seriesName}:${params.value}${type === 'duration' ? '' : '次'}
+
占比:${params.percent.toFixed(2)}%
+
+ `;
+ }
+ },
+ series: [
+ {
+ name: seriesName,
+ type: 'pie',
+ radius: ['50%', '70%'],
+ center: ['50%', '55%'],
+ color: CHART_CONFIG.pieColors,
+ label: {
+ show: true,
+ position: 'outside',
+ distance: 15,
+ fontSize: 11,
+ color: CHART_CONFIG.lightFontColor,
+ formatter: (params) => {
+ const truncatedName = this.truncateText(params.name, 8);
+ return `${truncatedName}(${params.value}${type === 'duration' ? '' : '次'}, ${params.percent.toFixed(1)}%)`;
+ },
+ align: 'center',
+ baseline: 'middle'
},
labelLine: {
+ show: true,
+ length: 15,
+ length2: 20,
lineStyle: {
- color: CHART_CONFIG.barColor, // 高亮时指示线颜色变为主色
- width: 1.5
+ color: '#ccc',
+ width: 1,
+ type: 'solid'
+ },
+ smooth: 0.2
+ },
+ data: pieData,
+ emphasis: {
+ itemStyle: {
+ shadowBlur: 10,
+ shadowColor: 'rgba(0, 0, 0, 0.1)'
+ },
+ label: {
+ color: CHART_CONFIG.fontColor,
+ fontSize: 12,
+ fontWeight: 500
+ },
+ labelLine: {
+ lineStyle: {
+ color: CHART_CONFIG.barColor,
+ width: 1.5
+ }
}
}
}
- }
- ]
- };
+ ]
+ };
- this.chartInstances.pie.setOption(option);
- // 监听窗口 resize
- this.addResizeListener('pie');
+ this.chartInstances.pie.setOption(option);
+ this.addResizeListener('pie');
+ } catch (error) {
+ console.error(`${seriesName}饼图初始化失败:`, error);
+ setTimeout(() => this.renderPieChart(type), 200);
+ }
},
- // 处理饼图数据(合并占比<5%的项)
- handlePieData(data) {
- const threshold = 5; // 阈值:5%
+
+ // 通用饼图数据处理(支持动态字段)
+ handlePieData(data, valueKey, ratioKey) {
+ const threshold = 5; // 占比低于5%合并为「其他」
let otherCount = 0;
const mainData = data.filter(item => {
- if (item.alarmCountRatio >= threshold) {
+ if (item[ratioKey] >= threshold) {
return true;
} else {
- otherCount += item.alarmCount;
+ otherCount += item[valueKey];
return false;
}
}).map(item => ({
name: item.alarmContent,
- value: item.alarmCount,
- ratio: item.alarmCountRatio
+ value: item[valueKey],
+ ratio: item[ratioKey]
}));
- // 如果有"其他"项,添加到数据中
if (otherCount > 0) {
mainData.push({
name: '其他',
@@ -387,57 +425,70 @@ export default {
return mainData;
},
- // 文本截断(超出长度显示省略号)
truncateText(text, maxLength) {
if (!text) return '';
return text.length > maxLength ? text.slice(0, maxLength) + '...' : text;
},
- // 添加窗口resize监听
addResizeListener(type) {
const chart = this.chartInstances[type];
if (chart) {
const resizeHandler = () => chart.resize();
window.addEventListener('resize', resizeHandler);
- // 存储resize处理器,用于后续移除
chart.resizeHandler = resizeHandler;
}
},
- // 销毁单个图表
destroyChart(type) {
const chart = this.chartInstances[type];
if (chart) {
- // 移除resize监听
window.removeEventListener('resize', chart.resizeHandler);
chart.dispose();
this.chartInstances[type] = null;
}
},
- // 销毁所有图表
destroyAllCharts() {
Object.keys(this.chartInstances).forEach(type => {
this.destroyChart(type);
});
},
- // 初始化弹窗数据
+ handleClose() {
+ this.destroyAllCharts();
+ this.formConfig[0].defaultSelect = [];
+ this.listQuery.startTime = undefined;
+ this.listQuery.endTime = undefined;
+ this.originData = null;
+ this.hasData = true;
+ if (this.$refs.searchBarForm) {
+ this.$refs.searchBarForm.form.timeVal = [];
+ }
+ },
+
init(data) {
this.dataForm = {
- equipmentId: data.equipmentId || undefined,
- equipmentName: data.equipmentName || '未知设备',
- lineId: data.lineId || '未知产线'
+ equipmentId: data.equipmentId || '',
+ equipmentName: data.equipmentName || '',
+ lineId: data.lineId || ''
};
+ this.activeLabel = 'duration'
this.listQuery.equipmentId = data.equipmentId || undefined;
this.visible = true;
+ this.originData = null;
+ this.hasData = false;
+
+ this.initDefaultDate();
+
this.$nextTick(() => {
- this.getDataList();
+ this.$nextTick(() => {
+ this.isDomReady = true;
+ this.getDataList();
+ });
});
}
},
- // 组件销毁时清理资源
beforeDestroy() {
this.destroyAllCharts();
}
@@ -445,6 +496,7 @@ export default {