|
@@ -0,0 +1,959 @@
|
|
|
|
|
+<template>
|
|
|
|
|
+ <div class="driving-container">
|
|
|
|
|
+ <!-- 页面头部 -->
|
|
|
|
|
+ <div class="page-header">
|
|
|
|
|
+ <h2>告警数据驾驶舱</h2>
|
|
|
|
|
+ <div class="header-right">
|
|
|
|
|
+ <el-date-picker v-model="currentDate" type="datetime" placeholder="选择日期时间" format="yyyy-MM-dd HH:mm:ss"
|
|
|
|
|
+ value-format="yyyy-MM-dd HH:mm:ss" style="width: 200px; margin-right: 10px">
|
|
|
|
|
+ </el-date-picker>
|
|
|
|
|
+ <el-button type="primary" icon="el-icon-refresh" @click="refreshData">刷新</el-button>
|
|
|
|
|
+ <el-checkbox v-model="autoRefresh" style="margin-left: 10px">自动刷新</el-checkbox>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 第一行:指标分类数据获取情况 + 实时告警列表 + 七日告警数据分布 -->
|
|
|
|
|
+ <div class="row row-1">
|
|
|
|
|
+ <!-- 指标分类数据获取情况 -->
|
|
|
|
|
+ <div class="col col-1">
|
|
|
|
|
+ <div class="card">
|
|
|
|
|
+ <div class="card-header">
|
|
|
|
|
+ <h3>指标分类数据获取情况</h3>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="card-body">
|
|
|
|
|
+ <el-tabs v-model="activeCategoryTab" @tab-click="handleTabClick">
|
|
|
|
|
+ <el-tab-pane label="内容类" name="content">
|
|
|
|
|
+ <div class="chart-wrapper">
|
|
|
|
|
+ <div ref="contentChart"></div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-tab-pane>
|
|
|
|
|
+ <el-tab-pane label="阈值类" name="threshold">
|
|
|
|
|
+ <div class="chart-wrapper">
|
|
|
|
|
+ <div ref="thresholdChart"></div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-tab-pane>
|
|
|
|
|
+ <el-tab-pane label="存在性" name="existence">
|
|
|
|
|
+ <div class="chart-wrapper">
|
|
|
|
|
+ <div ref="existenceChart"></div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </el-tab-pane>
|
|
|
|
|
+ </el-tabs>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 实时告警列表 -->
|
|
|
|
|
+ <div class="col col-2">
|
|
|
|
|
+ <div class="card">
|
|
|
|
|
+ <div class="card-header">
|
|
|
|
|
+ <h3>实时告警列表</h3>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="card-body">
|
|
|
|
|
+ <div class="alarm-list" ref="alarmList">
|
|
|
|
|
+ <div class="alarm-items-container">
|
|
|
|
|
+ <div v-for="alarm in realTimeAlarms" :key="alarm.id" class="alarm-item">
|
|
|
|
|
+ <div class="alarm-indicator">{{ alarm.indicator }}</div>
|
|
|
|
|
+ <div class="alarm-time">{{ alarm.occurTime }}/{{ alarm.alarmType }}</div>
|
|
|
|
|
+ <div class="alarm-status">
|
|
|
|
|
+ <el-tag type="danger"> 未处理 </el-tag>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 七日告警数据分布 -->
|
|
|
|
|
+ <div class="col col-3">
|
|
|
|
|
+ <div class="card">
|
|
|
|
|
+ <div class="card-header">
|
|
|
|
|
+ <h3>七日告警数据分布</h3>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="card-body">
|
|
|
|
|
+ <div class="total-alarms">
|
|
|
|
|
+ <div class="total-label">七日内总告警数</div>
|
|
|
|
|
+ <div class="total-count">{{ sevenDayTotalAlarms }}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="chart-wrapper">
|
|
|
|
|
+ <div ref="distributionChart"></div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="legend">
|
|
|
|
|
+ <div v-for="item in distributionLegend" :key="item.name" class="legend-item">
|
|
|
|
|
+ <span class="legend-color" :style="{ backgroundColor: item.color }"></span>
|
|
|
|
|
+ <span class="legend-text">{{ item.name }}: {{ item.value }}%</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 第二行:七日告警趋势分析 + 各系统七日告警 -->
|
|
|
|
|
+ <div class="row row-2">
|
|
|
|
|
+ <!-- 七日告警趋势分析 -->
|
|
|
|
|
+ <div class="col col-4">
|
|
|
|
|
+ <div class="card">
|
|
|
|
|
+ <div class="card-header">
|
|
|
|
|
+ <h3>七日告警趋势分析</h3>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="card-body">
|
|
|
|
|
+ <div class="chart-wrapper">
|
|
|
|
|
+ <div ref="trendChart"></div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- 各系统七日告警 -->
|
|
|
|
|
+ <div class="col col-5">
|
|
|
|
|
+ <div class="card">
|
|
|
|
|
+ <div class="card-header">
|
|
|
|
|
+ <h3>各系统七日告警</h3>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="card-body">
|
|
|
|
|
+ <div class="chart-wrapper">
|
|
|
|
|
+ <div ref="systemChart"></div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+</template>
|
|
|
|
|
+
|
|
|
|
|
+<script>
|
|
|
|
|
+import * as echarts from 'echarts'
|
|
|
|
|
+import { getRealTimeAlarm } from '@/api/newApi/driving'
|
|
|
|
|
+
|
|
|
|
|
+export default {
|
|
|
|
|
+ data() {
|
|
|
|
|
+ return {
|
|
|
|
|
+ currentDate: new Date().toISOString().slice(0, 19).replace('T', ' '),
|
|
|
|
|
+ autoRefresh: false,
|
|
|
|
|
+ refreshInterval: null,
|
|
|
|
|
+ activeCategoryTab: 'content',
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ realTimeAlarms: [],//实时告警列表
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ sevenDayTotalAlarms: 365,
|
|
|
|
|
+
|
|
|
|
|
+ distributionLegend: [
|
|
|
|
|
+ { name: 'CPU使用率', value: 30, color: '#5470c6' },
|
|
|
|
|
+ { name: '内存使用率', value: 25, color: '#91cc75' },
|
|
|
|
|
+ { name: '磁盘空间', value: 20, color: '#fac858' },
|
|
|
|
|
+ { name: '网络延迟', value: 15, color: '#ee6666' },
|
|
|
|
|
+ { name: '服务可用性', value: 10, color: '#73c0de' }
|
|
|
|
|
+ ],
|
|
|
|
|
+
|
|
|
|
|
+ // 图表实例
|
|
|
|
|
+ charts: {}
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ mounted() {
|
|
|
|
|
+
|
|
|
|
|
+ this.initCharts()
|
|
|
|
|
+ // 只有在autoRefresh为true时才启动自动刷新
|
|
|
|
|
+ if (this.autoRefresh) {
|
|
|
|
|
+ this.startAutoRefresh()
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ beforeDestroy() {
|
|
|
|
|
+ this.stopAutoRefresh()
|
|
|
|
|
+ this.destroyCharts()
|
|
|
|
|
+ },
|
|
|
|
|
+ watch: {
|
|
|
|
|
+ autoRefresh(val) {
|
|
|
|
|
+ if (val) {
|
|
|
|
|
+ this.startAutoRefresh()
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.stopAutoRefresh()
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ methods: {
|
|
|
|
|
+ getRealEvent() {
|
|
|
|
|
+ // 初始化实时告警列表
|
|
|
|
|
+ getRealTimeAlarm().then(res => {
|
|
|
|
|
+ if (res.code === 200) {
|
|
|
|
|
+ this.realTimeAlarms = res.data || []
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ },
|
|
|
|
|
+ initCharts() {
|
|
|
|
|
+ this.initCategoryCharts()
|
|
|
|
|
+ this.initDistributionChart()
|
|
|
|
|
+ this.initTrendChart()
|
|
|
|
|
+ this.initSystemChart()
|
|
|
|
|
+ this.getRealEvent()
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ initCategoryCharts() {
|
|
|
|
|
+ // 初始化当前激活的选项卡对应的图表
|
|
|
|
|
+ this.initCategoryChart(this.activeCategoryTab)
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ initDistributionChart() {
|
|
|
|
|
+ const chartRef = this.$refs.distributionChart
|
|
|
|
|
+ console.log('初始化分布图表:', chartRef)
|
|
|
|
|
+ if (!chartRef) {
|
|
|
|
|
+ console.error('未找到分布图表引用')
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 确保容器有正确的尺寸
|
|
|
|
|
+ chartRef.style.width = '100%'
|
|
|
|
|
+ chartRef.style.height = '100%'
|
|
|
|
|
+ chartRef.style.minHeight = '200px'
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ const distributionChart = echarts.init(chartRef)
|
|
|
|
|
+ console.log('分布图表初始化成功')
|
|
|
|
|
+ distributionChart.setOption({
|
|
|
|
|
+ tooltip: {
|
|
|
|
|
+ trigger: 'item',
|
|
|
|
|
+ formatter: '{a} <br/>{b}: {c} ({d}%)'
|
|
|
|
|
+ },
|
|
|
|
|
+ series: [
|
|
|
|
|
+ {
|
|
|
|
|
+ name: '告警分布',
|
|
|
|
|
+ type: 'pie',
|
|
|
|
|
+ radius: '60%',
|
|
|
|
|
+ center: ['50%', '50%'],
|
|
|
|
|
+ data: [
|
|
|
|
|
+ { value: 30, name: 'CPU使用率', itemStyle: { color: '#5470c6' } },
|
|
|
|
|
+ { value: 25, name: '内存使用率', itemStyle: { color: '#91cc75' } },
|
|
|
|
|
+ { value: 20, name: '磁盘空间', itemStyle: { color: '#fac858' } },
|
|
|
|
|
+ { value: 15, name: '网络延迟', itemStyle: { color: '#ee6666' } },
|
|
|
|
|
+ { value: 10, name: '服务可用性', itemStyle: { color: '#73c0de' } }
|
|
|
|
|
+ ],
|
|
|
|
|
+ emphasis: {
|
|
|
|
|
+ itemStyle: {
|
|
|
|
|
+ shadowBlur: 10,
|
|
|
|
|
+ shadowOffsetX: 0,
|
|
|
|
|
+ shadowColor: 'rgba(0, 0, 0, 0.5)'
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ ]
|
|
|
|
|
+ })
|
|
|
|
|
+ this.charts.distributionChart = distributionChart
|
|
|
|
|
+ console.log('分布图表配置设置成功')
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('初始化分布图表时出错:', error)
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ initTrendChart() {
|
|
|
|
|
+ const chartRef = this.$refs.trendChart
|
|
|
|
|
+ console.log('初始化趋势图表:', chartRef)
|
|
|
|
|
+ if (!chartRef) {
|
|
|
|
|
+ console.error('未找到趋势图表引用')
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 确保容器有正确的尺寸
|
|
|
|
|
+ chartRef.style.width = '100%'
|
|
|
|
|
+ chartRef.style.height = '100%'
|
|
|
|
|
+ chartRef.style.minHeight = '200px'
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ const trendChart = echarts.init(chartRef)
|
|
|
|
|
+ console.log('趋势图表初始化成功')
|
|
|
|
|
+ trendChart.setOption({
|
|
|
|
|
+ tooltip: {
|
|
|
|
|
+ trigger: 'axis',
|
|
|
|
|
+ axisPointer: {
|
|
|
|
|
+ type: 'cross',
|
|
|
|
|
+ label: {
|
|
|
|
|
+ backgroundColor: '#6a7985'
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ legend: {
|
|
|
|
|
+ data: ['内容类', '阈值类', '存在性']
|
|
|
|
|
+ },
|
|
|
|
|
+ grid: {
|
|
|
|
|
+ left: '3%',
|
|
|
|
|
+ right: '4%',
|
|
|
|
|
+ bottom: '3%',
|
|
|
|
|
+ containLabel: true
|
|
|
|
|
+ },
|
|
|
|
|
+ xAxis: [
|
|
|
|
|
+ {
|
|
|
|
|
+ type: 'category',
|
|
|
|
|
+ boundaryGap: false,
|
|
|
|
|
+ data: ['11-07', '11-08', '11-09', '11-10', '11-11', '11-12', '11-13']
|
|
|
|
|
+ }
|
|
|
|
|
+ ],
|
|
|
|
|
+ yAxis: [
|
|
|
|
|
+ {
|
|
|
|
|
+ type: 'value'
|
|
|
|
|
+ }
|
|
|
|
|
+ ],
|
|
|
|
|
+ series: [
|
|
|
|
|
+ {
|
|
|
|
|
+ name: '内容类',
|
|
|
|
|
+ type: 'line',
|
|
|
|
|
+ stack: 'Total',
|
|
|
|
|
+ areaStyle: {},
|
|
|
|
|
+ emphasis: {
|
|
|
|
|
+ focus: 'series'
|
|
|
|
|
+ },
|
|
|
|
|
+ data: [12, 19, 3, 5, 2, 3, 15],
|
|
|
|
|
+ lineStyle: {
|
|
|
|
|
+ color: '#ee6666'
|
|
|
|
|
+ },
|
|
|
|
|
+ itemStyle: {
|
|
|
|
|
+ color: '#ee6666'
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ name: '阈值类',
|
|
|
|
|
+ type: 'line',
|
|
|
|
|
+ stack: 'Total',
|
|
|
|
|
+ areaStyle: {},
|
|
|
|
|
+ emphasis: {
|
|
|
|
|
+ focus: 'series'
|
|
|
|
|
+ },
|
|
|
|
|
+ data: [11, 22, 33, 44, 55, 43, 32],
|
|
|
|
|
+ lineStyle: {
|
|
|
|
|
+ color: '#5470c6'
|
|
|
|
|
+ },
|
|
|
|
|
+ itemStyle: {
|
|
|
|
|
+ color: '#5470c6'
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ name: '存在性',
|
|
|
|
|
+ type: 'line',
|
|
|
|
|
+ stack: 'Total',
|
|
|
|
|
+ areaStyle: {},
|
|
|
|
|
+ emphasis: {
|
|
|
|
|
+ focus: 'series'
|
|
|
|
|
+ },
|
|
|
|
|
+ data: [12, 21, 15, 18, 22, 25, 20],
|
|
|
|
|
+ lineStyle: {
|
|
|
|
|
+ color: '#91cc75'
|
|
|
|
|
+ },
|
|
|
|
|
+ itemStyle: {
|
|
|
|
|
+ color: '#91cc75'
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ ]
|
|
|
|
|
+ })
|
|
|
|
|
+ this.charts.trendChart = trendChart
|
|
|
|
|
+ console.log('趋势图表配置设置成功')
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('初始化趋势图表时出错:', error)
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ initSystemChart() {
|
|
|
|
|
+ const chartRef = this.$refs.systemChart
|
|
|
|
|
+ console.log('初始化系统图表:', chartRef)
|
|
|
|
|
+ if (!chartRef) {
|
|
|
|
|
+ console.error('未找到系统图表引用')
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 确保容器有正确的尺寸
|
|
|
|
|
+ chartRef.style.width = '100%'
|
|
|
|
|
+ chartRef.style.height = '100%'
|
|
|
|
|
+ chartRef.style.minHeight = '200px'
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ const systemChart = echarts.init(chartRef)
|
|
|
|
|
+ console.log('系统图表初始化成功')
|
|
|
|
|
+ systemChart.setOption({
|
|
|
|
|
+ tooltip: {
|
|
|
|
|
+ trigger: 'axis',
|
|
|
|
|
+ axisPointer: {
|
|
|
|
|
+ type: 'shadow'
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ legend: {
|
|
|
|
|
+ data: ['内容类', '阈值类', '存在性']
|
|
|
|
|
+ },
|
|
|
|
|
+ grid: {
|
|
|
|
|
+ left: '3%',
|
|
|
|
|
+ right: '4%',
|
|
|
|
|
+ bottom: '3%',
|
|
|
|
|
+ containLabel: true
|
|
|
|
|
+ },
|
|
|
|
|
+ xAxis: [
|
|
|
|
|
+ {
|
|
|
|
|
+ type: 'category',
|
|
|
|
|
+ data: ['系统1', '系统2', '系统3', '系统4']
|
|
|
|
|
+ }
|
|
|
|
|
+ ],
|
|
|
|
|
+ yAxis: [
|
|
|
|
|
+ {
|
|
|
|
|
+ type: 'value'
|
|
|
|
|
+ }
|
|
|
|
|
+ ],
|
|
|
|
|
+ series: [
|
|
|
|
|
+ {
|
|
|
|
|
+ name: '内容类',
|
|
|
|
|
+ type: 'bar',
|
|
|
|
|
+ stack: 'total',
|
|
|
|
|
+ emphasis: {
|
|
|
|
|
+ focus: 'series'
|
|
|
|
|
+ },
|
|
|
|
|
+ data: [32, 28, 35, 25],
|
|
|
|
|
+ itemStyle: {
|
|
|
|
|
+ color: '#ee6666'
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ name: '阈值类',
|
|
|
|
|
+ type: 'bar',
|
|
|
|
|
+ stack: 'total',
|
|
|
|
|
+ emphasis: {
|
|
|
|
|
+ focus: 'series'
|
|
|
|
|
+ },
|
|
|
|
|
+ data: [45, 40, 38, 42],
|
|
|
|
|
+ itemStyle: {
|
|
|
|
|
+ color: '#5470c6'
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ {
|
|
|
|
|
+ name: '存在性',
|
|
|
|
|
+ type: 'bar',
|
|
|
|
|
+ stack: 'total',
|
|
|
|
|
+ emphasis: {
|
|
|
|
|
+ focus: 'series'
|
|
|
|
|
+ },
|
|
|
|
|
+ data: [18, 22, 15, 20],
|
|
|
|
|
+ itemStyle: {
|
|
|
|
|
+ color: '#91cc75'
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ ]
|
|
|
|
|
+ })
|
|
|
|
|
+ this.charts.systemChart = systemChart
|
|
|
|
|
+ console.log('系统图表配置设置成功')
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('初始化系统图表时出错:', error)
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ handleTabClick(tab) {
|
|
|
|
|
+ // 当切换选项卡时,确保图表能够正确显示
|
|
|
|
|
+ setTimeout(() => {
|
|
|
|
|
+ // 无论之前是否存在图表实例,都重新初始化图表
|
|
|
|
|
+ if (tab.name === 'content' && this.$refs.contentChart) {
|
|
|
|
|
+ // 销毁旧图表实例
|
|
|
|
|
+ if (this.charts.contentChart) {
|
|
|
|
|
+ this.charts.contentChart.dispose()
|
|
|
|
|
+ this.charts.contentChart = null
|
|
|
|
|
+ }
|
|
|
|
|
+ // 创建新图表实例
|
|
|
|
|
+ this.initCategoryChart('content')
|
|
|
|
|
+ } else if (tab.name === 'threshold' && this.$refs.thresholdChart) {
|
|
|
|
|
+ // 销毁旧图表实例
|
|
|
|
|
+ if (this.charts.thresholdChart) {
|
|
|
|
|
+ this.charts.thresholdChart.dispose()
|
|
|
|
|
+ this.charts.thresholdChart = null
|
|
|
|
|
+ }
|
|
|
|
|
+ // 创建新图表实例
|
|
|
|
|
+ this.initCategoryChart('threshold')
|
|
|
|
|
+ } else if (tab.name === 'existence' && this.$refs.existenceChart) {
|
|
|
|
|
+ // 销毁旧图表实例
|
|
|
|
|
+ if (this.charts.existenceChart) {
|
|
|
|
|
+ this.charts.existenceChart.dispose()
|
|
|
|
|
+ this.charts.existenceChart = null
|
|
|
|
|
+ }
|
|
|
|
|
+ // 创建新图表实例
|
|
|
|
|
+ this.initCategoryChart('existence')
|
|
|
|
|
+ }
|
|
|
|
|
+ }, 100)
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ initCategoryChart(type) {
|
|
|
|
|
+ // 初始化单个分类图表
|
|
|
|
|
+ const chartRef = this.$refs[`${type}Chart`]
|
|
|
|
|
+ console.log('初始化图表类型:', type, '图表引用:', chartRef)
|
|
|
|
|
+ if (!chartRef) {
|
|
|
|
|
+ console.error('未找到图表引用,类型:', type)
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 确保容器有正确的尺寸
|
|
|
|
|
+ chartRef.style.width = '100%'
|
|
|
|
|
+ chartRef.style.height = '100%'
|
|
|
|
|
+ chartRef.style.minHeight = '200px'
|
|
|
|
|
+
|
|
|
|
|
+ try {
|
|
|
|
|
+ const chart = echarts.init(chartRef)
|
|
|
|
|
+ console.log('图表初始化成功:', type)
|
|
|
|
|
+ let option = {}
|
|
|
|
|
+
|
|
|
|
|
+ if (type === 'content') {
|
|
|
|
|
+ option = {
|
|
|
|
|
+ tooltip: {
|
|
|
|
|
+ trigger: 'item',
|
|
|
|
|
+ formatter: '{a} <br/>{b}: {c} ({d}%)'
|
|
|
|
|
+ },
|
|
|
|
|
+ legend: {
|
|
|
|
|
+ orient: 'vertical',
|
|
|
|
|
+ left: 10,
|
|
|
|
|
+ data: ['正常', '无数据', '未配置']
|
|
|
|
|
+ },
|
|
|
|
|
+ series: [
|
|
|
|
|
+ {
|
|
|
|
|
+ name: '内容类',
|
|
|
|
|
+ type: 'pie',
|
|
|
|
|
+ radius: ['40%', '70%'],
|
|
|
|
|
+ avoidLabelOverlap: false,
|
|
|
|
|
+ itemStyle: {
|
|
|
|
|
+ borderRadius: 10,
|
|
|
|
|
+ borderColor: '#fff',
|
|
|
|
|
+ borderWidth: 2
|
|
|
|
|
+ },
|
|
|
|
|
+ label: {
|
|
|
|
|
+ show: false,
|
|
|
|
|
+ position: 'center'
|
|
|
|
|
+ },
|
|
|
|
|
+ emphasis: {
|
|
|
|
|
+ label: {
|
|
|
|
|
+ show: true,
|
|
|
|
|
+ fontSize: '18',
|
|
|
|
|
+ fontWeight: 'bold'
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ labelLine: {
|
|
|
|
|
+ show: false
|
|
|
|
|
+ },
|
|
|
|
|
+ data: [
|
|
|
|
|
+ { value: 15, name: '正常', itemStyle: { color: '#91cc75' } },
|
|
|
|
|
+ { value: 3, name: '无数据', itemStyle: { color: '#ee6666' } },
|
|
|
|
|
+ { value: 2, name: '未配置', itemStyle: { color: '#d9d9d9' } }
|
|
|
|
|
+ ]
|
|
|
|
|
+ }
|
|
|
|
|
+ ]
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (type === 'threshold') {
|
|
|
|
|
+ option = {
|
|
|
|
|
+ tooltip: {
|
|
|
|
|
+ trigger: 'item',
|
|
|
|
|
+ formatter: '{a} <br/>{b}: {c} ({d}%)'
|
|
|
|
|
+ },
|
|
|
|
|
+ legend: {
|
|
|
|
|
+ orient: 'vertical',
|
|
|
|
|
+ left: 10,
|
|
|
|
|
+ data: ['正常', '无数据', '未配置']
|
|
|
|
|
+ },
|
|
|
|
|
+ series: [
|
|
|
|
|
+ {
|
|
|
|
|
+ name: '阈值类',
|
|
|
|
|
+ type: 'pie',
|
|
|
|
|
+ radius: ['40%', '70%'],
|
|
|
|
|
+ avoidLabelOverlap: false,
|
|
|
|
|
+ itemStyle: {
|
|
|
|
|
+ borderRadius: 10,
|
|
|
|
|
+ borderColor: '#fff',
|
|
|
|
|
+ borderWidth: 2
|
|
|
|
|
+ },
|
|
|
|
|
+ label: {
|
|
|
|
|
+ show: false,
|
|
|
|
|
+ position: 'center'
|
|
|
|
|
+ },
|
|
|
|
|
+ emphasis: {
|
|
|
|
|
+ label: {
|
|
|
|
|
+ show: true,
|
|
|
|
|
+ fontSize: '18',
|
|
|
|
|
+ fontWeight: 'bold'
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ labelLine: {
|
|
|
|
|
+ show: false
|
|
|
|
|
+ },
|
|
|
|
|
+ data: [
|
|
|
|
|
+ { value: 12, name: '正常', itemStyle: { color: '#91cc75' } },
|
|
|
|
|
+ { value: 5, name: '无数据', itemStyle: { color: '#ee6666' } },
|
|
|
|
|
+ { value: 3, name: '未配置', itemStyle: { color: '#d9d9d9' } }
|
|
|
|
|
+ ]
|
|
|
|
|
+ }
|
|
|
|
|
+ ]
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (type === 'existence') {
|
|
|
|
|
+ option = {
|
|
|
|
|
+ tooltip: {
|
|
|
|
|
+ trigger: 'item',
|
|
|
|
|
+ formatter: '{a} <br/>{b}: {c} ({d}%)'
|
|
|
|
|
+ },
|
|
|
|
|
+ legend: {
|
|
|
|
|
+ orient: 'vertical',
|
|
|
|
|
+ left: 10,
|
|
|
|
|
+ data: ['正常', '无数据', '未配置']
|
|
|
|
|
+ },
|
|
|
|
|
+ series: [
|
|
|
|
|
+ {
|
|
|
|
|
+ name: '存在性',
|
|
|
|
|
+ type: 'pie',
|
|
|
|
|
+ radius: ['40%', '70%'],
|
|
|
|
|
+ avoidLabelOverlap: false,
|
|
|
|
|
+ itemStyle: {
|
|
|
|
|
+ borderRadius: 10,
|
|
|
|
|
+ borderColor: '#fff',
|
|
|
|
|
+ borderWidth: 2
|
|
|
|
|
+ },
|
|
|
|
|
+ label: {
|
|
|
|
|
+ show: false,
|
|
|
|
|
+ position: 'center'
|
|
|
|
|
+ },
|
|
|
|
|
+ emphasis: {
|
|
|
|
|
+ label: {
|
|
|
|
|
+ show: true,
|
|
|
|
|
+ fontSize: '18',
|
|
|
|
|
+ fontWeight: 'bold'
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ labelLine: {
|
|
|
|
|
+ show: false
|
|
|
|
|
+ },
|
|
|
|
|
+ data: [
|
|
|
|
|
+ { value: 18, name: '正常', itemStyle: { color: '#91cc75' } },
|
|
|
|
|
+ { value: 2, name: '无数据', itemStyle: { color: '#ee6666' } },
|
|
|
|
|
+ { value: 0, name: '未配置', itemStyle: { color: '#d9d9d9' } }
|
|
|
|
|
+ ]
|
|
|
|
|
+ }
|
|
|
|
|
+ ]
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if (option && chart) {
|
|
|
|
|
+ chart.setOption(option)
|
|
|
|
|
+ this.charts[`${type}Chart`] = chart
|
|
|
|
|
+ console.log('图表配置设置成功:', type)
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('初始化图表时出错:', type, error)
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ refreshData() {
|
|
|
|
|
+ // 模拟数据刷新
|
|
|
|
|
+ console.log('刷新数据...')
|
|
|
|
|
+ this.currentDate = new Date().toISOString().slice(0, 19).replace('T', ' ')
|
|
|
|
|
+
|
|
|
|
|
+ // 随机更新实时告警列表
|
|
|
|
|
+ this.getRealEvent()
|
|
|
|
|
+
|
|
|
|
|
+ // 重新初始化图表以确保正确显示
|
|
|
|
|
+ setTimeout(() => {
|
|
|
|
|
+ this.destroyCharts()
|
|
|
|
|
+ this.initCharts()
|
|
|
|
|
+ }, 100)
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ startAutoRefresh() {
|
|
|
|
|
+ this.stopAutoRefresh()
|
|
|
|
|
+ this.refreshInterval = setInterval(() => {
|
|
|
|
|
+ this.refreshData()
|
|
|
|
|
+ }, 5000)
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ stopAutoRefresh() {
|
|
|
|
|
+ if (this.refreshInterval) {
|
|
|
|
|
+ clearInterval(this.refreshInterval)
|
|
|
|
|
+ this.refreshInterval = null
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ resizeCharts() {
|
|
|
|
|
+ Object.values(this.charts).forEach(chart => {
|
|
|
|
|
+ chart.resize()
|
|
|
|
|
+ })
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ destroyCharts() {
|
|
|
|
|
+ Object.values(this.charts).forEach(chart => {
|
|
|
|
|
+ chart.dispose()
|
|
|
|
|
+ })
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ formatTime(date) {
|
|
|
|
|
+ const year = date.getFullYear()
|
|
|
|
|
+ const month = String(date.getMonth() + 1).padStart(2, '0')
|
|
|
|
|
+ const day = String(date.getDate()).padStart(2, '0')
|
|
|
|
|
+ const hours = String(date.getHours()).padStart(2, '0')
|
|
|
|
|
+ const minutes = String(date.getMinutes()).padStart(2, '0')
|
|
|
|
|
+ return `${year}-${month}-${day} ${hours}:${minutes}`
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+</script>
|
|
|
|
|
+
|
|
|
|
|
+<style scoped>
|
|
|
|
|
+.driving-container {
|
|
|
|
|
+ padding: 20px;
|
|
|
|
|
+ background-color: #f5f7fa;
|
|
|
|
|
+ min-height: 100vh;
|
|
|
|
|
+ height: 100vh;
|
|
|
|
|
+ box-sizing: border-box;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.page-header {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ margin-bottom: 20px;
|
|
|
|
|
+ padding: 15px 20px;
|
|
|
|
|
+ background-color: #ffffff;
|
|
|
|
|
+ border-radius: 4px;
|
|
|
|
|
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.page-header h2 {
|
|
|
|
|
+ margin: 0;
|
|
|
|
|
+ color: #303133;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.header-right {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.row {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ gap: 20px;
|
|
|
|
|
+ margin-bottom: 20px;
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ min-height: 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.row-1 {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ min-height: 300px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.row-2 {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ min-height: 350px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.col {
|
|
|
|
|
+ background-color: #ffffff;
|
|
|
|
|
+ border-radius: 4px;
|
|
|
|
|
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ min-width: 0;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.col-1,
|
|
|
|
|
+.col-2,
|
|
|
|
|
+.col-3,
|
|
|
|
|
+.col-4,
|
|
|
|
|
+.col-5 {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.card {
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.card-header {
|
|
|
|
|
+ padding: 15px 20px;
|
|
|
|
|
+ border-bottom: 1px solid #ebeef5;
|
|
|
|
|
+ background-color: #fafafa;
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.card-header h3 {
|
|
|
|
|
+ margin: 0;
|
|
|
|
|
+ font-size: 16px;
|
|
|
|
|
+ color: #303133;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.card-body {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ padding: 20px;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.el-tabs {
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.el-tabs__header {
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.el-tabs__content {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.el-tab-pane {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.chart-wrapper {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ min-height: 200px;
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.chart-wrapper>div {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+ min-height: 200px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.alarm-list {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.alarm-items-container {
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+ animation: scroll 20s linear infinite;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.alarm-item {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ padding: 10px;
|
|
|
|
|
+ border-bottom: 1px solid #ebeef5;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.alarm-indicator {
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ font-weight: 500;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.alarm-time {
|
|
|
|
|
+ width: 180px;
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ color: #909399;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.alarm-status {
|
|
|
|
|
+ width: 80px;
|
|
|
|
|
+ text-align: right;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.total-alarms {
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+ margin-bottom: 20px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.total-label {
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ color: #606266;
|
|
|
|
|
+ margin-bottom: 5px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.total-count {
|
|
|
|
|
+ font-size: 36px;
|
|
|
|
|
+ font-weight: bold;
|
|
|
|
|
+ color: #303133;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.legend {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-wrap: wrap;
|
|
|
|
|
+ gap: 10px;
|
|
|
|
|
+ margin-top: 10px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.legend-item {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.legend-color {
|
|
|
|
|
+ width: 12px;
|
|
|
|
|
+ height: 12px;
|
|
|
|
|
+ border-radius: 2px;
|
|
|
|
|
+ margin-right: 5px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+@keyframes scroll {
|
|
|
|
|
+ 0% {
|
|
|
|
|
+ transform: translateY(0);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ 100% {
|
|
|
|
|
+ transform: translateY(-100%);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 响应式设计 */
|
|
|
|
|
+@media screen and (max-width: 1200px) {
|
|
|
|
|
+ .driving-container {
|
|
|
|
|
+ height: auto;
|
|
|
|
|
+ min-height: 100vh;
|
|
|
|
|
+ overflow: auto;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .row {
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ height: auto;
|
|
|
|
|
+ min-height: 400px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .row-1,
|
|
|
|
|
+ .row-2 {
|
|
|
|
|
+ min-height: 400px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .col {
|
|
|
|
|
+ min-height: 400px;
|
|
|
|
|
+ margin-bottom: 20px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .chart-wrapper {
|
|
|
|
|
+ min-height: 300px;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+@media screen and (max-width: 768px) {
|
|
|
|
|
+ .page-header {
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ align-items: flex-start;
|
|
|
|
|
+ gap: 10px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .header-right {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .driving-container {
|
|
|
|
|
+ padding: 10px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .card-body {
|
|
|
|
|
+ padding: 15px;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .chart-wrapper {
|
|
|
|
|
+ min-height: 250px;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+</style>
|