Files
maintenance_system/src/views/pvSystem/alarmAnalysis/components/data.vue
re-JZzzz f0609716bc 1.完成生产管理-电量分析静态界面
2.完成综合管理-人员排班管理交互
3.修改部分逻辑和样式
2025-09-22 16:15:50 +08:00

527 lines
12 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="data-dashboard">
<!-- 标题区域 -->
<el-row class="dashboard-header">
<el-col :span="12">
<TitleComponent title="实时数据" :fontLevel="2" />
</el-col>
<el-col :span="12" class="text-right">
<span class="update-time">截止至2025/06/30 12:00</span>
</el-col>
</el-row>
<!-- 关键指标卡片区域 -->
<el-row class="metrics-container" :gutter="0">
<el-col v-for="card in cardData" :key="card.key" :span="6">
<div class="metric-card">
<div class="metric-value">{{ props.dashboardData[card.key] }}</div>
<div class="metric-label">{{ card.label }}</div>
<div class="metric-change">较上周 <span :class="props.dashboardData.updates[card.updateKey].type">
<img v-if="props.dashboardData.updates[card.updateKey].type === 'up'" src="/src/assets/demo/up.png"
class="trend-icon" alt="上升">
<img v-else src="/src/assets/demo/down.png" class="trend-icon" alt="下降">{{
props.dashboardData.updates[card.updateKey].value }}
</span>
</div>
</div>
</el-col>
</el-row>
<!-- 报警趋势图表区域 -->
<el-row class="trend-container">
<!-- 使用弹性布局实现标题在左下拉框在右 -->
<div class="trend-header-flex">
<TitleComponent title="报警趋势" :fontLevel="2" />
<el-select v-model="timeRange" placeholder="近7天" size="small">
<el-option label="近7天" value="7days" />
<el-option label="近30天" value="30days" />
<el-option label="近90天" value="90days" />
</el-select>
</div>
<el-col :span="24">
<div class="trend-section">
<el-row :gutter="20">
<!-- 报警数量图表 -->
<el-col :span="12">
<div class="chart-container">
<div class="chart-left-right-layout">
<div class="chart-info-container">
<div class="chart-title">报警数量() </div>
<div class="chart-total">{{ props.chartData.totals.alarmCount }}</div>
<div class="chart-value">
<span>较昨日</span>
<img v-if="props.chartData.dailyChanges.processEfficiency.type === 'up'"
src="/src/assets/demo/up.png" class="trend-icon" alt="上升">
<img v-else src="/src/assets/demo/down.png" class="trend-icon" alt="下降">
<span :class="props.chartData.dailyChanges.processEfficiency.type">
{{ props.chartData.dailyChanges.processEfficiency.value }}
</span>
</div>
</div>
<div ref="alarmCountRef" class="chart-content"></div>
</div>
</div>
</el-col>
<!-- 报警处理效率图表 -->
<el-col :span="12">
<div class="chart-container">
<div class="chart-left-right-layout">
<div class="chart-info-container">
<div class="chart-title">报警处理效率(%)</div>
<div class="chart-total">{{ props.chartData.totals.processEfficiency }}</div>
<div class="chart-value">
<span>较昨日</span>
<img v-if="props.chartData.dailyChanges.processEfficiency.type === 'up'"
src="/src/assets/demo/up.png" class="trend-icon" alt="上升">
<img v-else src="/src/assets/demo/down.png" class="trend-icon" alt="下降">
<span :class="props.chartData.dailyChanges.processEfficiency.type">
{{ props.chartData.dailyChanges.processEfficiency.value }}
</span>
</div>
</div>
<div ref="processEfficiencyRef" class="chart-content"></div>
</div>
</div>
</el-col>
</el-row>
</div>
</el-col>
</el-row>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted, watch } from 'vue';
import * as echarts from 'echarts';
import TitleComponent from '@/components/TitleComponent/index.vue';
// 定义props
const props = defineProps({
dashboardData: {
type: Object,
default: () => ({
todayAlarmTotal: 25,
unhandledAlarms: 8,
handledAlarms: 16,
avgProcessTime: '42分钟',
updates: {
todayAlarmTotal: { value: '4.2%', type: 'down' },
unhandledAlarms: { value: '5%', type: 'up' },
handledAlarms: { value: '8%', type: 'down' },
avgProcessTime: { value: '10%', type: 'down' }
}
})
},
chartData: {
type: Object,
default: () => ({
alarmCount: [150, 230, 224, 218, 135, 147, 260],
processEfficiency: [85, 88, 90, 87, 89, 91, 89],
dates: ['04/21', '04/22', '04/23', '04/24', '04/25', '04/26', '04/27'],
totals: {
alarmCount: 56,
processEfficiency: '89%'
},
dailyChanges: {
alarmCount: { value: '0.9%', type: 'up' },
processEfficiency: { value: '0.9%', type: 'down' }
}
})
}
});
// 卡片数据配置
const cardData = [
{
key: 'todayAlarmTotal',
label: '今日报警总数',
updateKey: 'todayAlarmTotal'
},
{
key: 'unhandledAlarms',
label: '未处理报警',
updateKey: 'unhandledAlarms'
},
{
key: 'handledAlarms',
label: '已处理报警',
updateKey: 'handledAlarms'
},
{
key: 'avgProcessTime',
label: '平均处理时长',
updateKey: 'avgProcessTime'
}
];
const timeRange = ref('7days');
const alarmCountRef = ref(null);
const processEfficiencyRef = ref(null);
let alarmCountChart = null;
let processEfficiencyChart = null;
// 初始化图表
const initCharts = () => {
// 报警数量图表
if (alarmCountRef.value) {
alarmCountChart = echarts.init(alarmCountRef.value);
}
// 报警处理效率图表
if (processEfficiencyRef.value) {
processEfficiencyChart = echarts.init(processEfficiencyRef.value);
}
// 设置报警数量图表配置
const alarmCountOption = {
tooltip: {
trigger: 'axis',
},
grid: {
left: '-38px',
right: '0%',
bottom: '0%',
top: '0%',
containLabel: true,
},
xAxis: {
type: 'category',
//文本颜色
axisLabel: {
textStyle: {
color: '#999999'
},
interval: 2 // 每隔三天显示一个标签索引从0开始间隔为2表示每3个数据点显示一个
},
// x轴线样式
axisLine: {
show: false,
lineStyle: {
color: "#427394"
}
},
axisTick: {
show: false
},
data: props.chartData.dates,
},
yAxis: {
type: 'value',
show: false,
axisLabel: {
textStyle: {
color: '#C5D6E6'
}
},
splitLine: {
show: false
}
},
series: [
{
data: props.chartData.alarmCount,
type: 'line',
smooth: true,
symbol: 'none',
lineStyle: {
color: '#3692FF',
width: 2
},
// 悬停时颜色
itemStyle: {
color: '#3692FF'
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: 'rgba(54, 146, 255, 0.3)'
},
{
offset: 1,
color: 'rgba(54, 146, 255, 0.05)'
}
])
}
}
]
};
// 设置报警处理效率图表配置
const processEfficiencyOption = {
tooltip: {
trigger: 'axis',
},
grid: {
left: '-38px',
right: '0%',
bottom: '0%',
top: '0%',
containLabel: true,
},
xAxis: {
type: 'category',
//文本颜色
axisLabel: {
textStyle: {
color: '#999999'
},
interval: 2 // 每隔三天显示一个标签索引从0开始间隔为2表示每3个数据点显示一个
},
// x轴线样式
axisLine: {
show: false,
lineStyle: {
color: "#427394"
}
},
axisTick: {
show: false
},
data: props.chartData.dates,
},
yAxis: {
type: 'value',
show: false,
axisLabel: {
textStyle: {
color: '#C5D6E6'
}
},
splitLine: {
show: false
}
},
series: [
{
data: props.chartData.processEfficiency,
type: 'line',
smooth: true,
symbol: 'none',
lineStyle: {
color: '#FF9900',
width: 2
},
// 悬停时颜色
itemStyle: {
color: '#FF9900'
},
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{
offset: 0,
color: 'rgba(255, 140, 0, 0.3)'
},
{
offset: 1,
color: 'rgba(255, 140, 0, 0.05)'
}
])
}
}
]
};
alarmCountChart.setOption(alarmCountOption);
processEfficiencyChart.setOption(processEfficiencyOption);
};
// 处理窗口大小变化
const handleResize = () => {
if (alarmCountChart) {
alarmCountChart.resize();
}
if (processEfficiencyChart) {
processEfficiencyChart.resize();
}
};
onMounted(() => {
initCharts();
window.addEventListener('resize', handleResize);
});
onUnmounted(() => {
window.removeEventListener('resize', handleResize);
if (alarmCountChart) {
alarmCountChart.dispose();
}
if (processEfficiencyChart) {
processEfficiencyChart.dispose();
}
});
</script>
<style scoped lang="scss">
.data-dashboard {
width: 100%;
height: 100%;
background: #fff;
padding: 0 20px;
box-sizing: border-box;
}
.dashboard-header {
align-items: center;
}
.dashboard-header h2 {
margin: 0;
font-size: 18px;
font-weight: 500;
color: #333;
}
.update-time {
font-size: 12px;
color: #999;
}
.metrics-container {
border-radius: 4px;
overflow: hidden;
background-color: rgba(242, 248, 252, 1);
background-color: rgba(242, 248, 252, 1);
}
.metric-card {
padding: 20px;
text-align: center;
position: relative;
height: 100%;
}
/* 移除最后一个卡片的分隔线 */
.metrics-container .el-col:last-child .metric-card::after {
display: none;
}
.metric-value {
font-size: 24px;
font-weight: bold;
color: #333;
margin-bottom: 8px;
}
.metric-label {
font-size: 14px;
color: #666;
margin-bottom: 4px;
}
.metric-change {
font-size: 12px;
color: #999;
}
.up {
color: #00B87A;
}
.down {
color: #ff4d4f;
}
.trend-icon {
// width: 12px;
// height: 12px;
margin-right: 2px;
vertical-align: middle;
}
.trend-container {
width: 100%;
}
.trend-header-flex {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
}
.trend-section {
background: #fff;
border-radius: 4px;
padding: 20px;
}
.trend-header {
align-items: center;
}
.trend-header h3 {
margin: 0;
font-size: 16px;
font-weight: 500;
color: #333;
}
.chart-container {
background: #fff;
border-radius: 4px;
display: flex;
align-items: center;
height: 150px;
}
.chart-left-right-layout {
display: flex;
align-items: center;
height: 100%;
width: 100%;
}
.chart-info-container {
width: 110px;
}
.chart-title {
font-size: 14px;
color: #666;
margin-bottom: 8px;
}
.chart-total {
font-size: 32px;
font-weight: bold;
color: #333;
margin-bottom: 8px;
}
.chart-value {
font-size: 12px;
color: #999;
margin-bottom: 0;
display: flex;
align-items: center;
gap: 4px;
line-height: 1;
}
.chart-value span {
display: inline-flex;
align-items: center;
vertical-align: middle;
}
.chart-value .trend-icon {
display: inline-flex;
align-items: center;
justify-content: center;
vertical-align: middle;
margin: 0;
}
.chart-content {
flex: 1;
height: 110px;
}
// Element Plus 样式覆盖
:deep(.el-select) {
width: 80px;
}
</style>