|
@@ -1,103 +1,103 @@
|
|
|
<template>
|
|
<template>
|
|
|
- <div class="play-dashboard-root">
|
|
|
|
|
- <div class="play-dashboard-page">
|
|
|
|
|
- <el-container>
|
|
|
|
|
- <el-header>
|
|
|
|
|
- <el-card shadow="hover" style="margin-top: 10px">
|
|
|
|
|
- <el-row justify="end" align="middle">
|
|
|
|
|
- <el-col :span="19" style="text-align: right">
|
|
|
|
|
- <el-radio-group v-model="timeRadio" size="small" @change="handleDateRangeChange">
|
|
|
|
|
- <!-- <el-radio-button label="今日" value="today" />-->
|
|
|
|
|
- <el-radio-button label="近7天" value="week"/>
|
|
|
|
|
- <el-radio-button label="近30天" value="month"/>
|
|
|
|
|
- <el-radio-button label="自定义" value="diy"/>
|
|
|
|
|
- </el-radio-group>
|
|
|
|
|
- </el-col>
|
|
|
|
|
- <el-col :span="5" style="text-align: right">
|
|
|
|
|
- <el-date-picker v-model="dateRange" type="daterange" @change="handleDateRangeChange" range-separator="-"
|
|
|
|
|
- start-placeholder="开始日期"
|
|
|
|
|
- :disabled="diyFlag" :clearable="false" end-placeholder="结束日期"
|
|
|
|
|
- style="margin-left: 10px; margin-right: 30px"/>
|
|
|
|
|
- </el-col>
|
|
|
|
|
- </el-row>
|
|
|
|
|
|
|
+ <el-container class="play-info-container" style="height: 90vh">
|
|
|
|
|
+ <el-header style="height: auto; padding: 20px;">
|
|
|
|
|
+ <el-card shadow="hover" class="header-card">
|
|
|
|
|
+ <el-row justify="end" align="middle">
|
|
|
|
|
+ <el-col :span="19" style="text-align: right">
|
|
|
|
|
+ <el-radio-group v-model="timeRadio" size="small" @change="handleDateRangeChange"
|
|
|
|
|
+ class="time-radio-group">
|
|
|
|
|
+ <el-radio-button label="近7天" value="week"/>
|
|
|
|
|
+ <el-radio-button label="近30天" value="month"/>
|
|
|
|
|
+ <el-radio-button label="自定义" value="diy"/>
|
|
|
|
|
+ </el-radio-group>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ <el-col :span="5" style="text-align: right">
|
|
|
|
|
+ <el-date-picker v-model="dateRange" type="daterange" @change="handleDateRangeChange" range-separator="-"
|
|
|
|
|
+ start-placeholder="开始日期"
|
|
|
|
|
+ :disabled="diyFlag" :clearable="false" end-placeholder="结束日期"
|
|
|
|
|
+ style="margin-left: 10px; margin-right: 30px"/>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ </el-row>
|
|
|
|
|
+ </el-card>
|
|
|
|
|
+ </el-header>
|
|
|
|
|
+
|
|
|
|
|
+ <el-main style="padding: 0 20px;">
|
|
|
|
|
+ <el-row :gutter="20">
|
|
|
|
|
+ <el-col :span="6" v-for="(item, index) in stats" :key="index">
|
|
|
|
|
+ <el-card shadow="hover" class="stat-card">
|
|
|
|
|
+ <h3 class="stat-title">{{ item.label }}</h3>
|
|
|
|
|
+ <p :class="['stat-number', item.class]">{{ item.value }}</p>
|
|
|
|
|
+ <div class="stat-card-footer"></div>
|
|
|
</el-card>
|
|
</el-card>
|
|
|
- </el-header>
|
|
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ </el-row>
|
|
|
|
|
|
|
|
- <el-main style="margin-top: 20px">
|
|
|
|
|
|
|
+ <el-row :gutter="20" style="margin-top: 20px;">
|
|
|
|
|
+ <el-col :span="18">
|
|
|
<el-row :gutter="20">
|
|
<el-row :gutter="20">
|
|
|
- <el-col :span="6" v-for="(item, index) in stats" :key="index">
|
|
|
|
|
- <el-card shadow="hover" class="stat-card">
|
|
|
|
|
- <h3>{{ item.label }}</h3>
|
|
|
|
|
- <p :class="['number', item.class]">{{ item.value }}</p>
|
|
|
|
|
|
|
+ <el-col :span="8">
|
|
|
|
|
+ <el-card shadow="hover" class="chart-card">
|
|
|
|
|
+ <h3 class="chart-title">类型占比</h3>
|
|
|
|
|
+ <div ref="typePie" class="chart-placeholder"></div>
|
|
|
|
|
+ </el-card>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ <el-col :span="8">
|
|
|
|
|
+ <el-card shadow="hover" class="chart-card">
|
|
|
|
|
+ <h3 class="chart-title">告警级别</h3>
|
|
|
|
|
+ <div ref="alarmLevel" class="chart-placeholder"></div>
|
|
|
|
|
+ </el-card>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ <el-col :span="8">
|
|
|
|
|
+ <el-card shadow="hover" class="chart-card">
|
|
|
|
|
+ <h3 class="chart-title">告警问题统计</h3>
|
|
|
|
|
+ <div ref="alarmCount" class="chart-placeholder"></div>
|
|
|
</el-card>
|
|
</el-card>
|
|
|
</el-col>
|
|
</el-col>
|
|
|
</el-row>
|
|
</el-row>
|
|
|
- <el-row :gutter="20" style="margin-top: 20px">
|
|
|
|
|
- <el-col :span="18">
|
|
|
|
|
- <el-row :gutter="20">
|
|
|
|
|
- <el-col :span="8">
|
|
|
|
|
- <el-card shadow="hover">
|
|
|
|
|
- <h3>类型占比</h3>
|
|
|
|
|
- <div ref="typePie" class="chart-placeholder"></div>
|
|
|
|
|
- </el-card>
|
|
|
|
|
- </el-col>
|
|
|
|
|
- <el-col :span="8">
|
|
|
|
|
- <el-card shadow="hover">
|
|
|
|
|
- <h3>告警级别</h3>
|
|
|
|
|
- <div ref="alarmLevel" class="chart-placeholder"></div>
|
|
|
|
|
- </el-card>
|
|
|
|
|
- </el-col>
|
|
|
|
|
- <el-col :span="8">
|
|
|
|
|
- <el-card shadow="hover">
|
|
|
|
|
- <h3>告警问题统计</h3>
|
|
|
|
|
- <div ref="alarmCount" class="chart-placeholder"></div>
|
|
|
|
|
- </el-card>
|
|
|
|
|
- </el-col>
|
|
|
|
|
- </el-row>
|
|
|
|
|
- <el-row :gutter="20" style="margin-top: 20px">
|
|
|
|
|
- <el-col :span="12">
|
|
|
|
|
- <el-card shadow="hover">
|
|
|
|
|
- <h3>在线时长排行</h3>
|
|
|
|
|
- <div ref="onlineTime" class="chart-placeholder"></div>
|
|
|
|
|
- </el-card>
|
|
|
|
|
- </el-col>
|
|
|
|
|
- <el-col :span="12">
|
|
|
|
|
- <el-card shadow="hover">
|
|
|
|
|
- <h3>告警数量排行</h3>
|
|
|
|
|
- <div ref="alarmNum" class="chart-placeholder"></div>
|
|
|
|
|
- </el-card>
|
|
|
|
|
- </el-col>
|
|
|
|
|
- </el-row>
|
|
|
|
|
|
|
+
|
|
|
|
|
+ <el-row :gutter="20" style="margin-top: 20px;">
|
|
|
|
|
+ <el-col :span="12">
|
|
|
|
|
+ <el-card shadow="hover" class="chart-card">
|
|
|
|
|
+ <h3 class="chart-title">在线时长排行</h3>
|
|
|
|
|
+ <div ref="onlineTime" class="chart-placeholder"></div>
|
|
|
|
|
+ </el-card>
|
|
|
</el-col>
|
|
</el-col>
|
|
|
- <el-col :span="6">
|
|
|
|
|
- <el-card shadow="hover">
|
|
|
|
|
- <h3>告警清单</h3>
|
|
|
|
|
- <el-table v-loading="loading" :data="alarmList" row-key="id">
|
|
|
|
|
- <el-table-column label="设备名称" align="left" prop="deviceName" width="100" :show-overflow-tooltip="true" />
|
|
|
|
|
- <el-table-column label="告警等级" align="center" prop="errorLevel" width="80">
|
|
|
|
|
- <template #default="scope">
|
|
|
|
|
- <dict-tag :options="smsb_device_error_level" :value="scope.row.errorLevel" />
|
|
|
|
|
- </template>
|
|
|
|
|
- </el-table-column>
|
|
|
|
|
- <el-table-column label="告警类型" align="center" prop="errorType" width="80">
|
|
|
|
|
- <template #default="scope">
|
|
|
|
|
- <dict-tag :options="smsb_device_error_type" :value="scope.row.errorType"/>
|
|
|
|
|
- </template>
|
|
|
|
|
- </el-table-column>
|
|
|
|
|
- <el-table-column label="创建时间" align="left" prop="createTime" width="160"/>
|
|
|
|
|
- </el-table>
|
|
|
|
|
|
|
+ <el-col :span="12">
|
|
|
|
|
+ <el-card shadow="hover" class="chart-card">
|
|
|
|
|
+ <h3 class="chart-title">告警数量排行</h3>
|
|
|
|
|
+ <div ref="alarmNum" class="chart-placeholder"></div>
|
|
|
</el-card>
|
|
</el-card>
|
|
|
</el-col>
|
|
</el-col>
|
|
|
</el-row>
|
|
</el-row>
|
|
|
- </el-main>
|
|
|
|
|
- </el-container>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+
|
|
|
|
|
+ <el-col :span="6">
|
|
|
|
|
+ <el-card shadow="hover" class="alarm-list-card">
|
|
|
|
|
+ <h3 class="chart-title">告警清单</h3>
|
|
|
|
|
+ <el-table v-loading="loading" :data="alarmList" row-key="id" class="alarm-table">
|
|
|
|
|
+ <el-table-column label="设备名称" align="left" prop="deviceName" width="100"
|
|
|
|
|
+ :show-overflow-tooltip="true"/>
|
|
|
|
|
+ <el-table-column label="告警等级" align="center" prop="errorLevel" width="80">
|
|
|
|
|
+ <template #default="scope">
|
|
|
|
|
+ <dict-tag :options="smsb_device_error_level" :value="scope.row.errorLevel"/>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column label="告警类型" align="center" prop="errorType" width="80">
|
|
|
|
|
+ <template #default="scope">
|
|
|
|
|
+ <dict-tag :options="smsb_device_error_type" :value="scope.row.errorType"/>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-table-column>
|
|
|
|
|
+ <el-table-column label="创建时间" align="left" prop="createTime" width="160"/>
|
|
|
|
|
+ </el-table>
|
|
|
|
|
+ </el-card>
|
|
|
|
|
+ </el-col>
|
|
|
|
|
+ </el-row>
|
|
|
|
|
+ </el-main>
|
|
|
|
|
+ </el-container>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
<script setup lang="ts">
|
|
|
-// console.log('[device.vue] setup called');
|
|
|
|
|
-import {reactive} from 'vue';
|
|
|
|
|
|
|
+import {ComponentInternalInstance, getCurrentInstance, onMounted, reactive, ref, toRefs} from 'vue';
|
|
|
import {deviceStatistics} from '@/api/smsb/device/device';
|
|
import {deviceStatistics} from '@/api/smsb/device/device';
|
|
|
import * as echarts from 'echarts';
|
|
import * as echarts from 'echarts';
|
|
|
import {DeviceErrorRecordQuery, DeviceErrorRecordVO} from '@/api/smsb/device/errorRecord_type';
|
|
import {DeviceErrorRecordQuery, DeviceErrorRecordVO} from '@/api/smsb/device/errorRecord_type';
|
|
@@ -109,6 +109,17 @@ import {
|
|
|
onlineTimeTop
|
|
onlineTimeTop
|
|
|
} from '@/api/smsb/device/errorRecord';
|
|
} from '@/api/smsb/device/errorRecord';
|
|
|
|
|
|
|
|
|
|
+// 处理坐标轴过长,保留5位
|
|
|
|
|
+const formatAxisValue = (value: number) => {
|
|
|
|
|
+ if (value.toString().length > 5) {
|
|
|
|
|
+ if (value >= 10000) {
|
|
|
|
|
+ return (value / 10000).toFixed(1) + '万';
|
|
|
|
|
+ }
|
|
|
|
|
+ return value.toString().slice(0, 5) + '...';
|
|
|
|
|
+ }
|
|
|
|
|
+ return value;
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
const alarmList = ref<DeviceErrorRecordVO[]>([]);
|
|
const alarmList = ref<DeviceErrorRecordVO[]>([]);
|
|
|
const {proxy} = getCurrentInstance() as ComponentInternalInstance;
|
|
const {proxy} = getCurrentInstance() as ComponentInternalInstance;
|
|
|
const {
|
|
const {
|
|
@@ -143,14 +154,11 @@ const stats = reactive([
|
|
|
]);
|
|
]);
|
|
|
|
|
|
|
|
const handleDateRangeChange = () => {
|
|
const handleDateRangeChange = () => {
|
|
|
- // console.log('[device.vue] handleDateRangeChange called');
|
|
|
|
|
const rangeType = timeRadio.value;
|
|
const rangeType = timeRadio.value;
|
|
|
const today = new Date();
|
|
const today = new Date();
|
|
|
const startDate = new Date();
|
|
const startDate = new Date();
|
|
|
const endDate = new Date();
|
|
const endDate = new Date();
|
|
|
switch (rangeType) {
|
|
switch (rangeType) {
|
|
|
- case 'today':
|
|
|
|
|
- break;
|
|
|
|
|
case 'week':
|
|
case 'week':
|
|
|
startDate.setDate(today.getDate() - 7);
|
|
startDate.setDate(today.getDate() - 7);
|
|
|
dateRange.value = [formatDate(startDate), formatDate(endDate)];
|
|
dateRange.value = [formatDate(startDate), formatDate(endDate)];
|
|
@@ -161,15 +169,15 @@ const handleDateRangeChange = () => {
|
|
|
dateRange.value = [formatDate(startDate), formatDate(endDate)];
|
|
dateRange.value = [formatDate(startDate), formatDate(endDate)];
|
|
|
diyFlag.value = true;
|
|
diyFlag.value = true;
|
|
|
break;
|
|
break;
|
|
|
- case "diy" :
|
|
|
|
|
|
|
+ case "diy":
|
|
|
diyFlag.value = false;
|
|
diyFlag.value = false;
|
|
|
- dateRange.value = [formatDate(new Date(dateRange.value[0])), formatDate(new Date(dateRange.value[1]))];
|
|
|
|
|
|
|
+ if (dateRange.value[0] && dateRange.value[1]) {
|
|
|
|
|
+ dateRange.value = [formatDate(new Date(dateRange.value[0])), formatDate(new Date(dateRange.value[1]))];
|
|
|
|
|
+ }
|
|
|
break;
|
|
break;
|
|
|
default:
|
|
default:
|
|
|
break;
|
|
break;
|
|
|
- // throw new Error('Invalid range type');
|
|
|
|
|
}
|
|
}
|
|
|
- console.log(dateRange.value);
|
|
|
|
|
alarmCountData();
|
|
alarmCountData();
|
|
|
getAlarmLevel();
|
|
getAlarmLevel();
|
|
|
getAlarmNumTop();
|
|
getAlarmNumTop();
|
|
@@ -182,33 +190,31 @@ const formatDate = (date: Date) => {
|
|
|
const day = String(date.getDate()).padStart(2, '0');
|
|
const day = String(date.getDate()).padStart(2, '0');
|
|
|
return `${year}-${month}-${day}`;
|
|
return `${year}-${month}-${day}`;
|
|
|
};
|
|
};
|
|
|
|
|
+
|
|
|
const getOnlineTimeTop = async () => {
|
|
const getOnlineTimeTop = async () => {
|
|
|
- // console.log('[device.vue] getOnlineTimeTop called');
|
|
|
|
|
const params = {
|
|
const params = {
|
|
|
startTime: dateRange.value[0],
|
|
startTime: dateRange.value[0],
|
|
|
endTime: dateRange.value[1]
|
|
endTime: dateRange.value[1]
|
|
|
};
|
|
};
|
|
|
const res = await onlineTimeTop(params);
|
|
const res = await onlineTimeTop(params);
|
|
|
- if (!onlineTime.value) {
|
|
|
|
|
- // console.log('[onlineTime] ref is null when initializing echarts');
|
|
|
|
|
- } else {
|
|
|
|
|
- // console.log('[onlineTime] ref:', onlineTime.value);
|
|
|
|
|
- echarts.dispose(onlineTime.value);
|
|
|
|
|
- // console.log('[onlineTime] echarts instance disposed before init');
|
|
|
|
|
- }
|
|
|
|
|
|
|
+
|
|
|
|
|
+ echarts.dispose(onlineTime.value);
|
|
|
const onlineTimeTopInstance = echarts.init(onlineTime.value, 'macaroons');
|
|
const onlineTimeTopInstance = echarts.init(onlineTime.value, 'macaroons');
|
|
|
- // console.log('[onlineTime] echarts instance created:', onlineTimeTopInstance);
|
|
|
|
|
|
|
+ // y文本截取函数
|
|
|
|
|
+ const truncateText = (text: string, maxLength: number = 15): string => {
|
|
|
|
|
+ if (!text) return '';
|
|
|
|
|
+ return text.length > maxLength ? text.substring(0, maxLength) + '...' : text;
|
|
|
|
|
+ };
|
|
|
|
|
+ // 格式化x轴数据
|
|
|
|
|
+ const formattedData = res.data.onlineTimeList.map(formatAxisValue);
|
|
|
|
|
+
|
|
|
onlineTimeTopInstance.setOption({
|
|
onlineTimeTopInstance.setOption({
|
|
|
- title: {
|
|
|
|
|
- text: ''
|
|
|
|
|
- },
|
|
|
|
|
|
|
+ title: {text: ''},
|
|
|
tooltip: {
|
|
tooltip: {
|
|
|
trigger: 'axis',
|
|
trigger: 'axis',
|
|
|
- axisPointer: {
|
|
|
|
|
- type: 'shadow'
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ axisPointer: {type: 'shadow'},
|
|
|
|
|
+ formatter: '{b}: {c}'
|
|
|
},
|
|
},
|
|
|
- legend: {},
|
|
|
|
|
grid: {
|
|
grid: {
|
|
|
left: '3%',
|
|
left: '3%',
|
|
|
right: '4%',
|
|
right: '4%',
|
|
@@ -217,48 +223,71 @@ const getOnlineTimeTop = async () => {
|
|
|
},
|
|
},
|
|
|
xAxis: {
|
|
xAxis: {
|
|
|
type: 'value',
|
|
type: 'value',
|
|
|
- boundaryGap: [0, 0.01]
|
|
|
|
|
|
|
+ boundaryGap: [0, 0.01],
|
|
|
|
|
+ axisLabel: {
|
|
|
|
|
+ formatter: function (value: number) {
|
|
|
|
|
+ return formatAxisValue(value);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
},
|
|
},
|
|
|
yAxis: {
|
|
yAxis: {
|
|
|
type: 'category',
|
|
type: 'category',
|
|
|
- data: res.data.deviceNameList
|
|
|
|
|
|
|
+ data: res.data.deviceNameList,
|
|
|
|
|
+ axisLabel: {
|
|
|
|
|
+ formatter: (value) => truncateText(value, 8) // 限制y轴标签最多显示5个字符
|
|
|
|
|
+ }
|
|
|
},
|
|
},
|
|
|
series: [
|
|
series: [
|
|
|
{
|
|
{
|
|
|
- name: '',
|
|
|
|
|
|
|
+ name: '在线时长',
|
|
|
type: 'bar',
|
|
type: 'bar',
|
|
|
- data: res.data.onlineTimeList
|
|
|
|
|
|
|
+ data: formattedData,
|
|
|
|
|
+ itemStyle: {
|
|
|
|
|
+ borderRadius: [0, 4, 4, 0],
|
|
|
|
|
+ color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
|
|
|
|
|
+ {offset: 0, color: '#409eff'},
|
|
|
|
|
+ {offset: 1, color: '#69b1ff'}
|
|
|
|
|
+ ])
|
|
|
|
|
+ },
|
|
|
|
|
+ emphasis: {
|
|
|
|
|
+ itemStyle: {
|
|
|
|
|
+ color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
|
|
|
|
|
+ {offset: 0, color: '#3a8ee6'},
|
|
|
|
|
+ {offset: 1, color: '#5d9cec'}
|
|
|
|
|
+ ])
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ animationDuration: 1000
|
|
|
}
|
|
}
|
|
|
]
|
|
]
|
|
|
});
|
|
});
|
|
|
|
|
+
|
|
|
|
|
+ // 响应窗口大小变化
|
|
|
|
|
+ window.addEventListener('resize', () => {
|
|
|
|
|
+ onlineTimeTopInstance.resize();
|
|
|
|
|
+ });
|
|
|
};
|
|
};
|
|
|
|
|
+
|
|
|
const getAlarmNumTop = async () => {
|
|
const getAlarmNumTop = async () => {
|
|
|
- // console.log('[device.vue] getAlarmNumTop called');
|
|
|
|
|
const params = {
|
|
const params = {
|
|
|
startTime: dateRange.value[0],
|
|
startTime: dateRange.value[0],
|
|
|
endTime: dateRange.value[1]
|
|
endTime: dateRange.value[1]
|
|
|
};
|
|
};
|
|
|
const res = await alarmNumTop(params);
|
|
const res = await alarmNumTop(params);
|
|
|
- if (!alarmNum.value) {
|
|
|
|
|
- // console.log('[alarmNum] ref is null when initializing echarts');
|
|
|
|
|
- } else {
|
|
|
|
|
- // console.log('[alarmNum] ref:', alarmNum.value);
|
|
|
|
|
- echarts.dispose(alarmNum.value);
|
|
|
|
|
- // console.log('[alarmNum] echarts instance disposed before init');
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // y文本截取函数
|
|
|
|
|
+ const truncateText = (text: string, maxLength: number = 15): string => {
|
|
|
|
|
+ if (!text) return '';
|
|
|
|
|
+ return text.length > maxLength ? text.substring(0, maxLength) + '...' : text;
|
|
|
|
|
+ };
|
|
|
|
|
+ echarts.dispose(alarmNum.value);
|
|
|
const alarmNumTopInstance = echarts.init(alarmNum.value, 'macaroons');
|
|
const alarmNumTopInstance = echarts.init(alarmNum.value, 'macaroons');
|
|
|
- // console.log('[alarmNum] echarts instance created:', alarmNumTopInstance);
|
|
|
|
|
|
|
+
|
|
|
alarmNumTopInstance.setOption({
|
|
alarmNumTopInstance.setOption({
|
|
|
- title: {
|
|
|
|
|
- text: ''
|
|
|
|
|
- },
|
|
|
|
|
|
|
+ title: {text: ''},
|
|
|
tooltip: {
|
|
tooltip: {
|
|
|
trigger: 'axis',
|
|
trigger: 'axis',
|
|
|
- axisPointer: {
|
|
|
|
|
- type: 'shadow'
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ axisPointer: {type: 'shadow'}
|
|
|
},
|
|
},
|
|
|
- legend: {},
|
|
|
|
|
grid: {
|
|
grid: {
|
|
|
left: '3%',
|
|
left: '3%',
|
|
|
right: '4%',
|
|
right: '4%',
|
|
@@ -267,87 +296,144 @@ const getAlarmNumTop = async () => {
|
|
|
},
|
|
},
|
|
|
xAxis: {
|
|
xAxis: {
|
|
|
type: 'value',
|
|
type: 'value',
|
|
|
- boundaryGap: [0, 0.01]
|
|
|
|
|
|
|
+ boundaryGap: [0, 0.01],
|
|
|
|
|
+ axisLabel: {
|
|
|
|
|
+ formatter: function (value: number) {
|
|
|
|
|
+ return formatAxisValue(value);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
},
|
|
},
|
|
|
yAxis: {
|
|
yAxis: {
|
|
|
type: 'category',
|
|
type: 'category',
|
|
|
- data: res.data.deviceNameList
|
|
|
|
|
|
|
+ data: res.data.deviceNameList,
|
|
|
|
|
+ axisLabel: {
|
|
|
|
|
+ formatter: (value) => truncateText(value, 8) // 限制y轴标签最多显示5个字符
|
|
|
|
|
+ }
|
|
|
},
|
|
},
|
|
|
series: [
|
|
series: [
|
|
|
{
|
|
{
|
|
|
- name: '',
|
|
|
|
|
|
|
+ name: '告警数量',
|
|
|
type: 'bar',
|
|
type: 'bar',
|
|
|
- data: res.data.alarmNumList
|
|
|
|
|
|
|
+ data: res.data.alarmNumList,
|
|
|
|
|
+ itemStyle: {
|
|
|
|
|
+ borderRadius: [0, 4, 4, 0],
|
|
|
|
|
+ color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
|
|
|
|
|
+ {offset: 0, color: '#f56c6c'},
|
|
|
|
|
+ {offset: 1, color: '#f88a8a'}
|
|
|
|
|
+ ])
|
|
|
|
|
+ },
|
|
|
|
|
+ emphasis: {
|
|
|
|
|
+ itemStyle: {
|
|
|
|
|
+ color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
|
|
|
|
|
+ {offset: 0, color: '#e34e4e'},
|
|
|
|
|
+ {offset: 1, color: '#ea6f6f'}
|
|
|
|
|
+ ])
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ animationDuration: 1000
|
|
|
}
|
|
}
|
|
|
]
|
|
]
|
|
|
});
|
|
});
|
|
|
|
|
+
|
|
|
|
|
+ window.addEventListener('resize', () => {
|
|
|
|
|
+ alarmNumTopInstance.resize();
|
|
|
|
|
+ });
|
|
|
};
|
|
};
|
|
|
|
|
+
|
|
|
const alarmCountData = async () => {
|
|
const alarmCountData = async () => {
|
|
|
- // console.log('[device.vue] alarmCountData called');
|
|
|
|
|
const params = {
|
|
const params = {
|
|
|
startTime: dateRange.value[0],
|
|
startTime: dateRange.value[0],
|
|
|
endTime: dateRange.value[1]
|
|
endTime: dateRange.value[1]
|
|
|
};
|
|
};
|
|
|
const res = await alarmCountByType(params);
|
|
const res = await alarmCountByType(params);
|
|
|
- if (!alarmCount.value) {
|
|
|
|
|
- // console.log('[alarmCount] ref is null when initializing echarts');
|
|
|
|
|
- } else {
|
|
|
|
|
- // console.log('[alarmCount] ref:', alarmCount.value);
|
|
|
|
|
- echarts.dispose(alarmCount.value);
|
|
|
|
|
- // console.log('[alarmCount] echarts instance disposed before init');
|
|
|
|
|
- }
|
|
|
|
|
|
|
+
|
|
|
|
|
+ echarts.dispose(alarmCount.value);
|
|
|
const alarmTypeInstance = echarts.init(alarmCount.value, 'macaroons');
|
|
const alarmTypeInstance = echarts.init(alarmCount.value, 'macaroons');
|
|
|
- // console.log('[alarmCount] echarts instance created:', alarmTypeInstance);
|
|
|
|
|
|
|
+
|
|
|
alarmTypeInstance.setOption({
|
|
alarmTypeInstance.setOption({
|
|
|
|
|
+ tooltip: {
|
|
|
|
|
+ trigger: 'axis',
|
|
|
|
|
+ axisPointer: {type: 'shadow'}
|
|
|
|
|
+ },
|
|
|
|
|
+ grid: {
|
|
|
|
|
+ left: '3%',
|
|
|
|
|
+ right: '4%',
|
|
|
|
|
+ bottom: '3%',
|
|
|
|
|
+ containLabel: true
|
|
|
|
|
+ },
|
|
|
xAxis: {
|
|
xAxis: {
|
|
|
type: 'category',
|
|
type: 'category',
|
|
|
- data: res.data.alarmType
|
|
|
|
|
|
|
+ data: res.data.alarmType,
|
|
|
|
|
+ axisLabel: {
|
|
|
|
|
+ rotate: 30,
|
|
|
|
|
+ interval: 0
|
|
|
|
|
+ }
|
|
|
},
|
|
},
|
|
|
yAxis: {
|
|
yAxis: {
|
|
|
- type: 'value'
|
|
|
|
|
|
|
+ type: 'value',
|
|
|
|
|
+ axisLabel: {
|
|
|
|
|
+ formatter: function (value: number) {
|
|
|
|
|
+ return formatAxisValue(value);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
},
|
|
},
|
|
|
series: [
|
|
series: [
|
|
|
{
|
|
{
|
|
|
data: res.data.alarmCount,
|
|
data: res.data.alarmCount,
|
|
|
- type: 'bar'
|
|
|
|
|
|
|
+ type: 'bar',
|
|
|
|
|
+ barWidth: '60%',
|
|
|
|
|
+ itemStyle: {
|
|
|
|
|
+ borderRadius: 4,
|
|
|
|
|
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
|
|
|
+ {offset: 0, color: '#67c23a'},
|
|
|
|
|
+ {offset: 1, color: '#85ce61'}
|
|
|
|
|
+ ])
|
|
|
|
|
+ },
|
|
|
|
|
+ emphasis: {
|
|
|
|
|
+ itemStyle: {
|
|
|
|
|
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
|
|
|
+ {offset: 0, color: '#52c41a'},
|
|
|
|
|
+ {offset: 1, color: '#73d13d'}
|
|
|
|
|
+ ])
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ animationDuration: 1000
|
|
|
}
|
|
}
|
|
|
]
|
|
]
|
|
|
});
|
|
});
|
|
|
|
|
+
|
|
|
|
|
+ window.addEventListener('resize', () => {
|
|
|
|
|
+ alarmTypeInstance.resize();
|
|
|
|
|
+ });
|
|
|
};
|
|
};
|
|
|
|
|
+
|
|
|
const getAlarmLevel = async () => {
|
|
const getAlarmLevel = async () => {
|
|
|
- // console.log('[device.vue] getAlarmLevel called');
|
|
|
|
|
const params = {
|
|
const params = {
|
|
|
startTime: dateRange.value[0],
|
|
startTime: dateRange.value[0],
|
|
|
endTime: dateRange.value[1]
|
|
endTime: dateRange.value[1]
|
|
|
};
|
|
};
|
|
|
const res = await alarmCountByLevel(params);
|
|
const res = await alarmCountByLevel(params);
|
|
|
|
|
|
|
|
- if (!alarmLevel.value) {
|
|
|
|
|
- // console.log('[alarmLevel] ref is null when initializing echarts');
|
|
|
|
|
- } else {
|
|
|
|
|
- // console.log('[alarmLevel] ref:', alarmLevel.value);
|
|
|
|
|
- echarts.dispose(alarmLevel.value);
|
|
|
|
|
- // console.log('[alarmLevel] echarts instance disposed before init');
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ echarts.dispose(alarmLevel.value);
|
|
|
const alarmLevelInstance = echarts.init(alarmLevel.value, 'macaroons');
|
|
const alarmLevelInstance = echarts.init(alarmLevel.value, 'macaroons');
|
|
|
- // console.log('[alarmLevel] echarts instance created:', alarmLevelInstance);
|
|
|
|
|
|
|
+
|
|
|
alarmLevelInstance.setOption({
|
|
alarmLevelInstance.setOption({
|
|
|
- title: {
|
|
|
|
|
- text: ''
|
|
|
|
|
- },
|
|
|
|
|
tooltip: {
|
|
tooltip: {
|
|
|
trigger: 'axis'
|
|
trigger: 'axis'
|
|
|
},
|
|
},
|
|
|
legend: {
|
|
legend: {
|
|
|
- data: ['普通', '紧急']
|
|
|
|
|
|
|
+ data: ['普通', '紧急'],
|
|
|
|
|
+ top: 0
|
|
|
},
|
|
},
|
|
|
grid: {
|
|
grid: {
|
|
|
left: '3%',
|
|
left: '3%',
|
|
|
right: '4%',
|
|
right: '4%',
|
|
|
bottom: '3%',
|
|
bottom: '3%',
|
|
|
|
|
+ top: '15%',
|
|
|
containLabel: true
|
|
containLabel: true
|
|
|
},
|
|
},
|
|
|
toolbox: {
|
|
toolbox: {
|
|
|
|
|
+ show: false,
|
|
|
feature: {
|
|
feature: {
|
|
|
saveAsImage: {}
|
|
saveAsImage: {}
|
|
|
}
|
|
}
|
|
@@ -355,29 +441,66 @@ const getAlarmLevel = async () => {
|
|
|
xAxis: {
|
|
xAxis: {
|
|
|
type: 'category',
|
|
type: 'category',
|
|
|
boundaryGap: false,
|
|
boundaryGap: false,
|
|
|
- data: res.data.alarmDateList
|
|
|
|
|
|
|
+ data: res.data.alarmDateList,
|
|
|
|
|
+ axisLabel: {
|
|
|
|
|
+ rotate: 30,
|
|
|
|
|
+ interval: 0
|
|
|
|
|
+ }
|
|
|
},
|
|
},
|
|
|
yAxis: {
|
|
yAxis: {
|
|
|
- type: 'value'
|
|
|
|
|
|
|
+ type: 'value',
|
|
|
|
|
+ axisLabel: {
|
|
|
|
|
+ formatter: function (value: number) {
|
|
|
|
|
+ return formatAxisValue(value);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
},
|
|
},
|
|
|
series: [
|
|
series: [
|
|
|
{
|
|
{
|
|
|
name: '普通',
|
|
name: '普通',
|
|
|
type: 'line',
|
|
type: 'line',
|
|
|
stack: 'Total',
|
|
stack: 'Total',
|
|
|
- data: res.data.normalAlamList
|
|
|
|
|
|
|
+ data: res.data.normalAlamList,
|
|
|
|
|
+ smooth: true,
|
|
|
|
|
+ symbol: 'circle',
|
|
|
|
|
+ symbolSize: 6,
|
|
|
|
|
+ lineStyle: {width: 2},
|
|
|
|
|
+ itemStyle: {color: '#409eff'},
|
|
|
|
|
+ areaStyle: {
|
|
|
|
|
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
|
|
|
+ {offset: 0, color: 'rgba(64, 158, 255, 0.3)'},
|
|
|
|
|
+ {offset: 1, color: 'rgba(64, 158, 255, 0)'}
|
|
|
|
|
+ ])
|
|
|
|
|
+ },
|
|
|
|
|
+ animationDuration: 1000
|
|
|
},
|
|
},
|
|
|
{
|
|
{
|
|
|
name: '紧急',
|
|
name: '紧急',
|
|
|
type: 'line',
|
|
type: 'line',
|
|
|
stack: 'Total',
|
|
stack: 'Total',
|
|
|
- data: res.data.dangerAlamList
|
|
|
|
|
|
|
+ data: res.data.dangerAlamList,
|
|
|
|
|
+ smooth: true,
|
|
|
|
|
+ symbol: 'circle',
|
|
|
|
|
+ symbolSize: 6,
|
|
|
|
|
+ lineStyle: {width: 2},
|
|
|
|
|
+ itemStyle: {color: '#f56c6c'},
|
|
|
|
|
+ areaStyle: {
|
|
|
|
|
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
|
|
|
+ {offset: 0, color: 'rgba(245, 108, 108, 0.3)'},
|
|
|
|
|
+ {offset: 1, color: 'rgba(245, 108, 108, 0)'}
|
|
|
|
|
+ ])
|
|
|
|
|
+ },
|
|
|
|
|
+ animationDuration: 1000
|
|
|
}
|
|
}
|
|
|
]
|
|
]
|
|
|
});
|
|
});
|
|
|
|
|
+
|
|
|
|
|
+ window.addEventListener('resize', () => {
|
|
|
|
|
+ alarmLevelInstance.resize();
|
|
|
|
|
+ });
|
|
|
};
|
|
};
|
|
|
|
|
+
|
|
|
const getDeviceStatistics = async () => {
|
|
const getDeviceStatistics = async () => {
|
|
|
- // console.log('[device.vue] getDeviceStatistics called');
|
|
|
|
|
const res = await deviceStatistics();
|
|
const res = await deviceStatistics();
|
|
|
stats.forEach((item) => {
|
|
stats.forEach((item) => {
|
|
|
switch (item.label) {
|
|
switch (item.label) {
|
|
@@ -395,59 +518,71 @@ const getDeviceStatistics = async () => {
|
|
|
break;
|
|
break;
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
- if (!typePie.value) {
|
|
|
|
|
- // console.log('[typePie] ref is null when initializing echarts');
|
|
|
|
|
- } else {
|
|
|
|
|
- // console.log('[typePie] ref:', typePie.value);
|
|
|
|
|
- echarts.dispose(typePie.value);
|
|
|
|
|
- // console.log('[typePie] echarts instance disposed before init');
|
|
|
|
|
- }
|
|
|
|
|
|
|
+
|
|
|
|
|
+ echarts.dispose(typePie.value);
|
|
|
const typePieInstance = echarts.init(typePie.value, 'macaroons');
|
|
const typePieInstance = echarts.init(typePie.value, 'macaroons');
|
|
|
- // console.log('[typePie] echarts instance created:', typePieInstance);
|
|
|
|
|
|
|
+
|
|
|
typePieInstance.setOption({
|
|
typePieInstance.setOption({
|
|
|
- title: {
|
|
|
|
|
- text: '',
|
|
|
|
|
- subtext: '',
|
|
|
|
|
- left: 'center'
|
|
|
|
|
- },
|
|
|
|
|
tooltip: {
|
|
tooltip: {
|
|
|
- trigger: 'item'
|
|
|
|
|
|
|
+ trigger: 'item',
|
|
|
|
|
+ formatter: '{a} <br/>{b}: {c} ({d}%)'
|
|
|
},
|
|
},
|
|
|
legend: {
|
|
legend: {
|
|
|
orient: 'vertical',
|
|
orient: 'vertical',
|
|
|
- left: 'left'
|
|
|
|
|
|
|
+ left: 'left',
|
|
|
|
|
+ top: 'center'
|
|
|
},
|
|
},
|
|
|
series: [
|
|
series: [
|
|
|
{
|
|
{
|
|
|
- name: '类型占比',
|
|
|
|
|
|
|
+ name: '设备状态',
|
|
|
type: 'pie',
|
|
type: 'pie',
|
|
|
- radius: '65%',
|
|
|
|
|
- data: [
|
|
|
|
|
- { value: (res.data.onlineNum / res.data.totalNum) * 100, name: '在线' },
|
|
|
|
|
- { value: (res.data.offlineNum / res.data.totalNum) * 100, name: '离线' },
|
|
|
|
|
- { value: (res.data.initNum / res.data.totalNum) * 100, name: '待接入' }
|
|
|
|
|
- ],
|
|
|
|
|
|
|
+ radius: ['40%', '70%'],
|
|
|
|
|
+ center: ['60%', '50%'],
|
|
|
|
|
+ avoidLabelOverlap: false,
|
|
|
|
|
+ itemStyle: {
|
|
|
|
|
+ borderRadius: 6,
|
|
|
|
|
+ borderColor: '#fff',
|
|
|
|
|
+ borderWidth: 2
|
|
|
|
|
+ },
|
|
|
|
|
+ label: {
|
|
|
|
|
+ show: false,
|
|
|
|
|
+ position: 'center'
|
|
|
|
|
+ },
|
|
|
emphasis: {
|
|
emphasis: {
|
|
|
- itemStyle: {
|
|
|
|
|
- shadowBlur: 10,
|
|
|
|
|
- shadowOffsetX: 0,
|
|
|
|
|
- shadowColor: 'rgba(0, 0, 0, 0.5)'
|
|
|
|
|
|
|
+ label: {
|
|
|
|
|
+ show: true,
|
|
|
|
|
+ fontSize: '16',
|
|
|
|
|
+ fontWeight: 'bold'
|
|
|
}
|
|
}
|
|
|
- }
|
|
|
|
|
|
|
+ },
|
|
|
|
|
+ labelLine: {
|
|
|
|
|
+ show: false
|
|
|
|
|
+ },
|
|
|
|
|
+ data: [
|
|
|
|
|
+ {value: res.data.onlineNum, name: '在线', itemStyle: {color: '#67c23a'}},
|
|
|
|
|
+ {value: res.data.offlineNum, name: '离线', itemStyle: {color: '#f56c6c'}},
|
|
|
|
|
+ {value: res.data.initNum, name: '待接入', itemStyle: {color: '#e6a23c'}}
|
|
|
|
|
+ ],
|
|
|
|
|
+ animationDuration: 1000,
|
|
|
|
|
+ animationEasingUpdate: 'quinticInOut'
|
|
|
}
|
|
}
|
|
|
]
|
|
]
|
|
|
});
|
|
});
|
|
|
|
|
+
|
|
|
|
|
+ window.addEventListener('resize', () => {
|
|
|
|
|
+ typePieInstance.resize();
|
|
|
|
|
+ });
|
|
|
};
|
|
};
|
|
|
|
|
+
|
|
|
const getAlarmList = async () => {
|
|
const getAlarmList = async () => {
|
|
|
- // console.log('[device.vue] getAlarmList called');
|
|
|
|
|
loading.value = true;
|
|
loading.value = true;
|
|
|
const res = await listDeviceErrorRecord(dialogQueryParams.value);
|
|
const res = await listDeviceErrorRecord(dialogQueryParams.value);
|
|
|
alarmList.value = res.rows;
|
|
alarmList.value = res.rows;
|
|
|
total.value = res.total;
|
|
total.value = res.total;
|
|
|
loading.value = false;
|
|
loading.value = false;
|
|
|
};
|
|
};
|
|
|
|
|
+
|
|
|
onMounted(() => {
|
|
onMounted(() => {
|
|
|
- // console.log('[device.vue] onMounted called');
|
|
|
|
|
handleDateRangeChange();
|
|
handleDateRangeChange();
|
|
|
getDeviceStatistics();
|
|
getDeviceStatistics();
|
|
|
getAlarmList();
|
|
getAlarmList();
|
|
@@ -455,35 +590,183 @@ onMounted(() => {
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
|
<style scoped>
|
|
<style scoped>
|
|
|
|
|
+
|
|
|
|
|
+.header-card {
|
|
|
|
|
+ margin-top: 20px;
|
|
|
|
|
+ background-color: #fff;
|
|
|
|
|
+ border: 1px solid #e6f7ff;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.time-radio-group {
|
|
|
|
|
+ display: inline-flex;
|
|
|
|
|
+ background-color: #fff;
|
|
|
|
|
+ border-radius: 4px;
|
|
|
|
|
+ padding: 2px;
|
|
|
|
|
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
.stat-card {
|
|
.stat-card {
|
|
|
text-align: center;
|
|
text-align: center;
|
|
|
|
|
+ transition: all 0.3s ease;
|
|
|
|
|
+ border-radius: 8px;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ background: #fff;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.stat-card:hover {
|
|
|
|
|
+ transform: translateY(-5px);
|
|
|
|
|
+ box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.number {
|
|
|
|
|
- font-size: 24px;
|
|
|
|
|
|
|
+.stat-title {
|
|
|
|
|
+ color: #666;
|
|
|
|
|
+ font-size: 16px;
|
|
|
|
|
+ margin-bottom: 12px;
|
|
|
font-weight: bold;
|
|
font-weight: bold;
|
|
|
|
|
+ padding-top: 10px;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.stat-number {
|
|
|
|
|
+ font-size: 28px;
|
|
|
|
|
+ font-weight: bold;
|
|
|
|
|
+ margin: 0;
|
|
|
|
|
+ padding: 10px 0;
|
|
|
|
|
+ transition: all 0.3s ease;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.stat-card-footer {
|
|
|
|
|
+ height: 4px;
|
|
|
|
|
+ width: 100%;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.success {
|
|
.success {
|
|
|
- color: green;
|
|
|
|
|
|
|
+ color: #67c23a;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.stat-card:has(.success) .stat-card-footer {
|
|
|
|
|
+ background-color: #67c23a;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.danger {
|
|
.danger {
|
|
|
- color: red;
|
|
|
|
|
|
|
+ color: #f56c6c;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.stat-card:has(.danger) .stat-card-footer {
|
|
|
|
|
+ background-color: #f56c6c;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.warning {
|
|
.warning {
|
|
|
- color: orange;
|
|
|
|
|
|
|
+ color: #e6a23c;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.stat-card:has(.warning) .stat-card-footer {
|
|
|
|
|
+ background-color: #e6a23c;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.chart-card {
|
|
|
|
|
+ border-radius: 8px;
|
|
|
|
|
+ transition: all 0.3s ease;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.play-info-container {
|
|
|
|
|
+ background-color: #f0f2f5;
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.chart-card:hover {
|
|
|
|
|
+ box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.chart-title {
|
|
|
|
|
+ color: #333;
|
|
|
|
|
+ font-size: 16px;
|
|
|
|
|
+ margin: 0 0 15px 0;
|
|
|
|
|
+ padding: 15px 15px 0;
|
|
|
|
|
+ font-weight: 500;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.chart-title::before {
|
|
|
|
|
+ content: '';
|
|
|
|
|
+ display: inline-block;
|
|
|
|
|
+ width: 4px;
|
|
|
|
|
+ height: 16px;
|
|
|
|
|
+ background-color: #409eff;
|
|
|
|
|
+ margin-right: 8px;
|
|
|
|
|
+ border-radius: 2px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.chart-placeholder {
|
|
.chart-placeholder {
|
|
|
height: 250px;
|
|
height: 250px;
|
|
|
- /*background: #f5f5f5;*/
|
|
|
|
|
border-radius: 8px;
|
|
border-radius: 8px;
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.alarm-list-card {
|
|
|
|
|
+ border-radius: 8px;
|
|
|
|
|
+ transition: all 0.3s ease;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ height: 100%;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.alarm-list-card:hover {
|
|
|
|
|
+ box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.alarm-table {
|
|
|
|
|
+ border-radius: 8px;
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.el-table__row {
|
|
|
|
|
+ transition: background-color 0.2s ease;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.el-table__row:hover {
|
|
|
|
|
+ background-color: #f5f7fa !important;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.el-radio-button__inner {
|
|
|
|
|
+ transition: all 0.2s ease;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.el-radio-button.is-active .el-radio-button__inner {
|
|
|
|
|
+ background-color: #409eff;
|
|
|
|
|
+ border-color: #409eff;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/* 动画效果 */
|
|
|
|
|
+@keyframes fadeIn {
|
|
|
|
|
+ from {
|
|
|
|
|
+ opacity: 0;
|
|
|
|
|
+ transform: translateY(10px);
|
|
|
|
|
+ }
|
|
|
|
|
+ to {
|
|
|
|
|
+ opacity: 1;
|
|
|
|
|
+ transform: translateY(0);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.stat-card, .chart-card, .alarm-list-card {
|
|
|
|
|
+ animation: fadeIn 0.5s ease forwards;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.stat-card:nth-child(1) {
|
|
|
|
|
+ animation-delay: 0.1s;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.stat-card:nth-child(2) {
|
|
|
|
|
+ animation-delay: 0.2s;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.stat-card:nth-child(3) {
|
|
|
|
|
+ animation-delay: 0.3s;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.play-dashboard-root {
|
|
|
|
|
- height: 100vh;
|
|
|
|
|
- overflow-y: auto;
|
|
|
|
|
|
|
+.stat-card:nth-child(4) {
|
|
|
|
|
+ animation-delay: 0.4s;
|
|
|
}
|
|
}
|
|
|
</style>
|
|
</style>
|