|
|
@@ -0,0 +1,1082 @@
|
|
|
+<template>
|
|
|
+ <div class="driving-container" v-loading="loading" element-loading-text="数据加载中..."
|
|
|
+ element-loading-spinner="el-icon-loading" element-loading-background="rgba(0, 0, 0, 0.8)">
|
|
|
+ <!-- 页面头部 -->
|
|
|
+ <div class="page-header">
|
|
|
+ <h2>告警数据驾驶舱</h2>
|
|
|
+ <div class="header-right">
|
|
|
+ <el-date-picker disabled 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">
|
|
|
+ <div class="category-charts-container">
|
|
|
+ <div class="chart-item">
|
|
|
+ <div ref="contentChart" class="chart"></div>
|
|
|
+ <div class="chart-title">内容类</div>
|
|
|
+ </div>
|
|
|
+ <div class="chart-item">
|
|
|
+ <div ref="thresholdChart" class="chart"></div>
|
|
|
+ <div class="chart-title">阈值类</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </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">{{this.alarmDistribution.reduce((acc, item) => acc + item.value, 0)}}</div>
|
|
|
+ </div> -->
|
|
|
+ <div class="chart-wrapper">
|
|
|
+ <div ref="distributionChart"></div>
|
|
|
+ </div>
|
|
|
+ <!-- <div class="legend">
|
|
|
+ <div v-for="item in alarmDistribution" :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, getAlarmDashboard } from '@/api/newApi/driving'
|
|
|
+import { getDashboardDashboard } from '@/api/newApi/driving'
|
|
|
+
|
|
|
+export default {
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ currentDate: new Date(),
|
|
|
+ autoRefresh: false,
|
|
|
+ refreshInterval: null,
|
|
|
+ activeCategoryTab: 'content',
|
|
|
+
|
|
|
+
|
|
|
+ loading: true,//全局加载状态
|
|
|
+ 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: {},
|
|
|
+
|
|
|
+
|
|
|
+ alarmDistribution: [],//告警分布
|
|
|
+ indicatorStatus: [],//指标状态 - 内容类
|
|
|
+ thresholdStatus: [],//指标状态 - 阈值类
|
|
|
+
|
|
|
+ alarmTrend: [],//七日告警趋势分析
|
|
|
+ systemAlarms: [],//各系统七日告警
|
|
|
+ }
|
|
|
+ },
|
|
|
+ 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: {
|
|
|
+ // async getRealEvent() {
|
|
|
+ // // 初始化实时告警列表
|
|
|
+ // try {
|
|
|
+ // const res = await getRealTimeAlarm()
|
|
|
+ // if (res.code === 200) {
|
|
|
+ // this.realTimeAlarms = res.data || []
|
|
|
+ // }
|
|
|
+ // } catch (error) {
|
|
|
+ // console.error('获取实时告警失败:', error)
|
|
|
+ // }
|
|
|
+ // },
|
|
|
+ async initCharts() {
|
|
|
+ this.loading = true
|
|
|
+ try {
|
|
|
+ await this.getAlarmEvent()
|
|
|
+ this.initCategoryCharts()
|
|
|
+ this.initDistributionChart()
|
|
|
+ // this.initTrendChart()
|
|
|
+ this.initSystemChart()
|
|
|
+ // await this.getRealEvent()
|
|
|
+ } finally {
|
|
|
+ this.loading = false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ async getAlarmEvent() {
|
|
|
+ try {
|
|
|
+ // const res = await getAlarmDashboard()
|
|
|
+ const res = await getDashboardDashboard()
|
|
|
+ if (res) {
|
|
|
+ console.log('告警数据驾驶舱全量数据:', res)
|
|
|
+ let resData = res.data
|
|
|
+ // if (res.alarmDistribution && res.alarmDistribution.length > 0) {
|
|
|
+ // this.alarmDistribution = res.alarmDistribution.map(item => ({
|
|
|
+ // value: item.count,
|
|
|
+ // name: item.indicator,
|
|
|
+ // }))
|
|
|
+ // console.log(this.alarmDistribution)
|
|
|
+ // }
|
|
|
+ if (resData.metricCategory) {
|
|
|
+ this.indicatorStatus = resData.metricCategory.content
|
|
|
+ console.log(this.indicatorStatus)
|
|
|
+ console.log('按类型分组的指标状态:', this.indicatorStatus)
|
|
|
+ }
|
|
|
+ if (resData.alarmTrend && resData.alarmTrend.length > 0) {
|
|
|
+ // 按照type分类
|
|
|
+ const groupedTrend = {}
|
|
|
+ resData.alarmTrend.forEach(item => {
|
|
|
+ if (!groupedTrend[item.type]) {
|
|
|
+ groupedTrend[item.type] = []
|
|
|
+ }
|
|
|
+ groupedTrend[item.type].push({
|
|
|
+ value: item.count,
|
|
|
+ name: item.date,
|
|
|
+ type: item.type,
|
|
|
+ })
|
|
|
+ })
|
|
|
+ this.alarmTrend = groupedTrend
|
|
|
+ console.log('按类型分组的告警趋势:', this.alarmTrend)
|
|
|
+ }
|
|
|
+ if (resData.systemAlarms && resData.systemAlarms.length > 0) {
|
|
|
+ // 按照systemName分类
|
|
|
+ const groupedSystem = {}
|
|
|
+ resData.systemAlarms.forEach(item => {
|
|
|
+ if (!groupedSystem[item.systemName]) {
|
|
|
+ groupedSystem[item.systemName] = {}
|
|
|
+ }
|
|
|
+ groupedSystem[item.systemName][item.type] = item.count
|
|
|
+ })
|
|
|
+ this.systemAlarms = groupedSystem
|
|
|
+ console.log('按系统分组的系统告警:', this.systemAlarms)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('获取告警数据失败:', error)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ initCategoryCharts() {
|
|
|
+ // 同时初始化内容类和阈值类图表
|
|
|
+ this.initCategoryChart('content')
|
|
|
+ this.initCategoryChart('threshold')
|
|
|
+ },
|
|
|
+
|
|
|
+ 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}%)'
|
|
|
+ },
|
|
|
+ legend: {
|
|
|
+ orient: 'vertical',
|
|
|
+ left: 'right'
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: '告警分布',
|
|
|
+ type: 'pie',
|
|
|
+ radius: ['40%', '70%'],
|
|
|
+ center: ['25%', '50%'],
|
|
|
+ label: {
|
|
|
+ show: true,
|
|
|
+ position: 'center',
|
|
|
+ formatter: function () {
|
|
|
+ const total = this.alarmDistribution.reduce((sum, item) => sum + item.value, 0);
|
|
|
+ return total + '\n七日告警数分布';
|
|
|
+ }.bind(this),
|
|
|
+ fontSize: 16,
|
|
|
+ },
|
|
|
+ data: this.alarmDistribution,
|
|
|
+ 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: Object.keys(this.alarmTrend)
|
|
|
+ },
|
|
|
+
|
|
|
+ grid: {
|
|
|
+ left: '3%',
|
|
|
+ right: '4%',
|
|
|
+ bottom: '3%',
|
|
|
+ containLabel: true
|
|
|
+ },
|
|
|
+ xAxis: [
|
|
|
+ {
|
|
|
+ type: 'category',
|
|
|
+ boundaryGap: false,
|
|
|
+ data: [...new Set(Object.values(this.alarmTrend).flat().map(item => item.name))]
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ yAxis: [
|
|
|
+ {
|
|
|
+ type: 'value'
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ // dataZoom: [
|
|
|
+ // {
|
|
|
+ // type: 'slider',
|
|
|
+ // orient: 'horizontal',
|
|
|
+ // xAxisIndex: 0,
|
|
|
+ // start: 0,
|
|
|
+ // end: 50
|
|
|
+ // }
|
|
|
+ // ],
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: '内容类',
|
|
|
+ type: 'line',
|
|
|
+ stack: 'Total',
|
|
|
+ areaStyle: {},
|
|
|
+ emphasis: {
|
|
|
+ focus: 'series'
|
|
|
+ },
|
|
|
+ data: (this.alarmTrend['内容类'] || []).map(item => item.value),
|
|
|
+ lineStyle: {
|
|
|
+ color: '#ee6666'
|
|
|
+ },
|
|
|
+ itemStyle: {
|
|
|
+ color: '#ee6666'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: '阈值类',
|
|
|
+ type: 'line',
|
|
|
+ stack: 'Total',
|
|
|
+ areaStyle: {},
|
|
|
+ emphasis: {
|
|
|
+ focus: 'series'
|
|
|
+ },
|
|
|
+ data: (this.alarmTrend['阈值类'] || []).map(item => item.value),
|
|
|
+ lineStyle: {
|
|
|
+ color: '#5470c6'
|
|
|
+ },
|
|
|
+ itemStyle: {
|
|
|
+ color: '#5470c6'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: '存在性',
|
|
|
+ type: 'line',
|
|
|
+ stack: 'Total',
|
|
|
+ areaStyle: {},
|
|
|
+ emphasis: {
|
|
|
+ focus: 'series'
|
|
|
+ },
|
|
|
+ data: this.alarmTrend['存在性'].map(item => item.value),
|
|
|
+ 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: Object.keys(this.systemAlarms || {})
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ yAxis: [
|
|
|
+ {
|
|
|
+ type: 'value'
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ dataZoom: [
|
|
|
+ {
|
|
|
+ type: 'slider',
|
|
|
+ orient: 'horizontal',
|
|
|
+ xAxisIndex: 0,
|
|
|
+ start: 0,
|
|
|
+ end: 50
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: '内容类',
|
|
|
+ type: 'bar',
|
|
|
+ emphasis: {
|
|
|
+ focus: 'series'
|
|
|
+ },
|
|
|
+ data: Object.keys(this.systemAlarms || {}).map(system => this.systemAlarms[system]['内容类'] || 0),
|
|
|
+ itemStyle: {
|
|
|
+ color: '#ee6666'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: '阈值类',
|
|
|
+ type: 'bar',
|
|
|
+ emphasis: {
|
|
|
+ focus: 'series'
|
|
|
+ },
|
|
|
+ data: Object.keys(this.systemAlarms || {}).map(system => this.systemAlarms[system]['阈值类'] || 0),
|
|
|
+ itemStyle: {
|
|
|
+ color: '#5470c6'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: '存在性',
|
|
|
+ type: 'bar',
|
|
|
+ emphasis: {
|
|
|
+ focus: 'series'
|
|
|
+ },
|
|
|
+ data: Object.keys(this.systemAlarms || {}).map(system => this.systemAlarms[system]['存在性'] || 0),
|
|
|
+ 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: 'horizontal',
|
|
|
+ bottom: 0,
|
|
|
+ data: ['正常', '无数据', '未配置'],
|
|
|
+ width: '100%',
|
|
|
+ itemWidth: 10,
|
|
|
+ itemHeight: 10,
|
|
|
+ textStyle: {
|
|
|
+ fontSize: 12
|
|
|
+ }
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: '内容类',
|
|
|
+ type: 'pie',
|
|
|
+ radius: ['40%', '70%'],
|
|
|
+ avoidLabelOverlap: false,
|
|
|
+ itemStyle: {
|
|
|
+ borderRadius: 0,
|
|
|
+ borderColor: '#fff',
|
|
|
+ borderWidth: 0
|
|
|
+ },
|
|
|
+ label: {
|
|
|
+ show: false
|
|
|
+ },
|
|
|
+ emphasis: {
|
|
|
+ label: {
|
|
|
+ show: false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ labelLine: {
|
|
|
+ show: false
|
|
|
+ },
|
|
|
+ data:
|
|
|
+ this.indicatorStatus,
|
|
|
+ color: ['#F44336', '#BDBDBD', '#8BC34A']
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ } else if (type === 'threshold') {
|
|
|
+ option = {
|
|
|
+ tooltip: {
|
|
|
+ trigger: 'item',
|
|
|
+ formatter: '{a} <br/>{b}: {c} ({d}%)'
|
|
|
+ },
|
|
|
+ legend: {
|
|
|
+ orient: 'horizontal',
|
|
|
+ bottom: 0,
|
|
|
+ data: ['正常', '无数据', '未配置'],
|
|
|
+ width: '100%',
|
|
|
+ itemWidth: 10,
|
|
|
+ itemHeight: 10,
|
|
|
+ textStyle: {
|
|
|
+ fontSize: 12
|
|
|
+ }
|
|
|
+ },
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ name: '阈值类',
|
|
|
+ type: 'pie',
|
|
|
+ radius: ['40%', '70%'],
|
|
|
+ avoidLabelOverlap: false,
|
|
|
+ itemStyle: {
|
|
|
+ borderRadius: 0,
|
|
|
+ borderColor: '#fff',
|
|
|
+ borderWidth: 0
|
|
|
+ },
|
|
|
+ label: {
|
|
|
+ show: false
|
|
|
+ },
|
|
|
+ emphasis: {
|
|
|
+ label: {
|
|
|
+ show: false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ labelLine: {
|
|
|
+ show: false
|
|
|
+ },
|
|
|
+ data:
|
|
|
+ this.indicatorStatus['阈值类'] || [
|
|
|
+ { value: 4, name: '正常' },
|
|
|
+ { value: 0, name: '无数据' },
|
|
|
+ { value: 6, name: '未配置' }
|
|
|
+ ],
|
|
|
+ color: ['#F44336', '#BDBDBD', '#8BC34A']
|
|
|
+
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ } 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: this.indicatorStatus['存在性']
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (option && chart) {
|
|
|
+ chart.setOption(option)
|
|
|
+ this.charts[`${type}Chart`] = chart
|
|
|
+ // console.log('图表配置设置成功:', type)
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('初始化图表时出错:', type, error)
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ async refreshData() {
|
|
|
+ // 模拟数据刷新
|
|
|
+ console.log('刷新数据...')
|
|
|
+ this.currentDate = new Date().toISOString().slice(0, 19).replace('T', ' ')
|
|
|
+ this.loading = true
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 随机更新实时告警列表
|
|
|
+ await this.getRealEvent()
|
|
|
+
|
|
|
+ // 重新初始化图表以确保正确显示
|
|
|
+ this.destroyCharts()
|
|
|
+ await this.initCharts()
|
|
|
+ } finally {
|
|
|
+ this.loading = false
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ 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;
|
|
|
+}
|
|
|
+
|
|
|
+.category-charts-container {
|
|
|
+ display: flex;
|
|
|
+ gap: 20px;
|
|
|
+ padding: 10px;
|
|
|
+ height: 100%;
|
|
|
+ min-height: 250px;
|
|
|
+}
|
|
|
+
|
|
|
+.chart-item {
|
|
|
+ flex: 1;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ height: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+.chart {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ min-height: 200px;
|
|
|
+}
|
|
|
+
|
|
|
+.chart-title {
|
|
|
+ margin-top: 10px;
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 500;
|
|
|
+ color: #333;
|
|
|
+ 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>
|