Merge branch 'tcy' of http://xny.yj-3d.com:3000/taoge_xiaodi/maintenance_system into dhr
BIN
src/assets/demo/avatar.png
Normal file
|
After Width: | Height: | Size: 7.4 KiB |
BIN
src/assets/demo/back.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
src/assets/demo/baojing.png
Normal file
|
After Width: | Height: | Size: 786 B |
BIN
src/assets/demo/gaojing.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/assets/demo/huojian.png
Normal file
|
After Width: | Height: | Size: 509 B |
BIN
src/assets/demo/more.png
Normal file
|
After Width: | Height: | Size: 250 B |
BIN
src/assets/demo/rain.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
src/assets/demo/shaixuan.png
Normal file
|
After Width: | Height: | Size: 222 B |
BIN
src/assets/demo/shandian.png
Normal file
|
After Width: | Height: | Size: 385 B |
BIN
src/assets/demo/upload.png
Normal file
|
After Width: | Height: | Size: 205 B |
BIN
src/assets/demo/use.png
Normal file
|
After Width: | Height: | Size: 539 B |
BIN
src/assets/demo/wcl.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
src/assets/demo/wendu.png
Normal file
|
After Width: | Height: | Size: 437 B |
BIN
src/assets/demo/yichuli.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
src/assets/demo/zzcl.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
src/assets/fonts/Alibaba/Alibaba-PuHuiTi-Bold.otf
Normal file
BIN
src/assets/fonts/Alibaba/Alibaba-PuHuiTi-Medium.otf
Normal file
BIN
src/assets/fonts/Alibaba/AlimamaShuHeiTi-Bold.otf
Normal file
BIN
src/assets/fonts/D-Din/D-DIN-Bold.ttf
Normal file
BIN
src/assets/fonts/D-Din/D-DIN-Italic.ttf
Normal file
BIN
src/assets/fonts/D-Din/D-DIN.ttf
Normal file
BIN
src/assets/fonts/D-Din/D-DINCondensed-Bold.ttf
Normal file
BIN
src/assets/fonts/D-Din/D-DINCondensed.ttf
Normal file
BIN
src/assets/fonts/D-Din/D-DINExp-Bold.ttf
Normal file
BIN
src/assets/fonts/D-Din/D-DINExp-Italic.ttf
Normal file
BIN
src/assets/fonts/D-Din/D-DINExp.ttf
Normal file
BIN
src/assets/fonts/DOUYU/DOUYU.TTF
Normal file
BIN
src/assets/fonts/DOUYU/斗鱼追光体2.0.ttf
Normal file
BIN
src/assets/fonts/PangmenTi/PangMenZhengDaoBiaoTiTi-1.ttf
Normal file
BIN
src/assets/fonts/ReflectTi/SourceHanSansCN-Light.otf
Normal file
BIN
src/assets/fonts/ReflectTi/SourceHanSansCN-Medium_0.otf
Normal file
BIN
src/assets/fonts/ReflectTi/SourceHanSansCN-Regular.otf
Normal file
BIN
src/assets/fonts/Roboto/Roboto-Regular.ttf
Normal file
BIN
src/assets/fonts/alimamashuheiti.ttf
Normal file
165
src/assets/fonts/fonts.scss
Normal file
@ -0,0 +1,165 @@
|
||||
@font-face {
|
||||
font-family: 'Rang_men_zheng'; //庞门正道字体
|
||||
src: url('./PangmenTi/PangMenZhengDaoBiaoTiTi-1.ttf');
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Rang_men_zheng_title'; //庞门正道标题体
|
||||
src: url('./PangMenZhengDaoBiaoTiTi/PangMenZhengDaoBiaoTiTi-1.ttf');
|
||||
font-weight: normal;
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
// 思源字体
|
||||
// @font-face {
|
||||
// font-family: 'SourceHanSansCN-Bold';
|
||||
// src: url('./ReflectTi/SourceHanSansCN-Bold_0.otf'); //暂时没用
|
||||
// font-weight: normal;
|
||||
// font-style: normal;
|
||||
// }
|
||||
// @font-face {
|
||||
// font-family: 'SourceHanSansCN-ExtraLight';
|
||||
// src: url('./ReflectTi/SourceHanSansCN-ExtraLight.otf');//暂时没用
|
||||
// font-weight: normal;
|
||||
// font-style: normal;
|
||||
// }
|
||||
// @font-face {
|
||||
// font-family: 'SourceHanSansCN-Heavy';
|
||||
// src: url('./ReflectTi/SourceHanSansCN-Heavy.otf');//暂时没用
|
||||
// font-weight: normal;
|
||||
// font-style: normal;
|
||||
// }
|
||||
// @font-face {
|
||||
// font-family: 'SourceHanSansCN-Light';
|
||||
// src: url('./ReflectTi/SourceHanSansCN-Light.otf');
|
||||
// font-weight: normal;
|
||||
// font-style: normal;
|
||||
// }
|
||||
// @font-face {
|
||||
// font-family: 'SourceHanSansCN-Medium';
|
||||
// src: url('./ReflectTi/SourceHanSansCN-Medium_0.otf');
|
||||
// font-weight: normal;
|
||||
// font-style: normal;
|
||||
// }
|
||||
|
||||
// @font-face {
|
||||
// font-family: 'SourceHanSansCN-Normal';
|
||||
// src: url('./ReflectTi/SourceHanSansCN-Normal.otf');//暂时没用
|
||||
// font-weight: normal;
|
||||
// font-style: normal;
|
||||
// }
|
||||
|
||||
@font-face {
|
||||
font-family: 'SourceHanSansCN-Regular';
|
||||
src: url('./ReflectTi/SourceHanSansCN-Regular.otf');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
// @font-face {
|
||||
// font-family: 'SourceHanSansCN-Bold';
|
||||
// src: url('./ReflectTi/SourceHanSerifCN-Bold.otf');//暂时没用
|
||||
// font-weight: normal;
|
||||
// font-style: normal;
|
||||
// }
|
||||
|
||||
// @font-face {
|
||||
// font-family: 'SourceHanSansCN-ExtraLight';
|
||||
// src: url('./ReflectTi/SourceHanSerifCN-ExtraLight.otf');//暂时没用
|
||||
// font-weight: normal;
|
||||
// font-style: normal;
|
||||
// }
|
||||
|
||||
// @font-face {
|
||||
// font-family: 'SourceHanSerifCN-Heavy';
|
||||
// src: url('./ReflectTi/SourceHanSerifCN-Heavy.otf');//暂时没用
|
||||
// font-weight: normal;
|
||||
// font-style: normal;
|
||||
// }
|
||||
|
||||
// @font-face {
|
||||
// font-family: 'SourceHanSerifCN-Light';
|
||||
// src: url('./ReflectTi/SourceHanSerifCN-Light.otf');//暂时没用
|
||||
// font-weight: normal;
|
||||
// font-style: normal;
|
||||
// }
|
||||
|
||||
// @font-face {
|
||||
// font-family: 'SourceHanSerifCN-Medium';
|
||||
// src: url('./ReflectTi/SourceHanSerifCN-Medium.otf');//暂时没用
|
||||
// font-weight: normal;
|
||||
// font-style: normal;
|
||||
// }
|
||||
// @font-face {
|
||||
// font-family: 'SourceHanSerifCN-Regular';
|
||||
// src: url('./ReflectTi/SourceHanSerifCN-Regular.otf');//暂时没用
|
||||
// font-weight: normal;
|
||||
// font-style: normal;
|
||||
// }
|
||||
// @font-face {
|
||||
// font-family: 'SourceHanSerifCN-SemiBold';
|
||||
// src: url('./ReflectTi/SourceHanSerifCN-SemiBold.otf');//暂时没用
|
||||
// font-weight: normal;
|
||||
// font-style: normal;
|
||||
// }
|
||||
|
||||
// 阿里巴巴普惠体
|
||||
@font-face {
|
||||
font-family: 'Alibaba-PuHuiTi-Bold';
|
||||
src: url('./Alibaba/Alibaba-PuHuiTi-Bold.otf');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
//阿里黑体
|
||||
@font-face {
|
||||
font-family: 'AlimamaShuHeiTi-Bold';
|
||||
src: url('./Alibaba/AlimamaShuHeiTi-Bold.otf');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
// @font-face {
|
||||
// font-family: 'Alibaba-PuHuiTi-Heavy';
|
||||
// src: url('./Alibaba/Alibaba-PuHuiTi-Heavy.otf');//暂时没用
|
||||
// font-weight: normal;
|
||||
// font-style: normal;
|
||||
// }
|
||||
// @font-face {
|
||||
// font-family: 'Alibaba-PuHuiTi-Light';
|
||||
// src: url('./Alibaba/Alibaba-PuHuiTi-Light.otf');//暂时没用
|
||||
// font-weight: normal;
|
||||
// font-style: normal;
|
||||
// }
|
||||
@font-face {
|
||||
font-family: 'Alibaba-PuHuiTi-Medium';
|
||||
src: url('./Alibaba/Alibaba-PuHuiTi-Medium.otf');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
// @font-face {
|
||||
// font-family: 'Alibaba-PuHuiTi-Regular';
|
||||
// src: url('./Alibaba/Alibaba-PuHuiTi-Regular.otf');//暂时没用
|
||||
// font-weight: normal;
|
||||
// font-style: normal;
|
||||
// }
|
||||
@font-face {
|
||||
font-family: 'DOUYUFont'; //斗鱼追光体
|
||||
src: url('./DouYu//斗鱼追光体2.0.ttf');
|
||||
font-weight: normal;
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'D-Din';
|
||||
src: url('./D-Din//D-DIN.ttf');
|
||||
font-weight: normal;
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Roboto-Regular'; //Roboto
|
||||
src: url('./Roboto//Roboto-Regular.ttf');
|
||||
font-weight: normal;
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
BIN
src/assets/fonts/庞门正道标题体2.0增强版.ttf
Normal file
|
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 1.5 KiB |
30
src/components/TitleComponent/index.vue
Normal file
@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<el-row>
|
||||
<el-col>
|
||||
<div style="color: rgba(0, 30, 59, 1);font-family: 'Alibaba-PuHuiTi-Bold';margin: 10px 0 0 0;"
|
||||
:style="{ fontSize: fontLevelMap[props.fontLevel] }">
|
||||
{{ props.title }}
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col>
|
||||
<p style="color: rgba(154, 154, 154, 1);font-size: 14px;">
|
||||
{{ props.subtitle }}
|
||||
</p>
|
||||
</el-col>
|
||||
|
||||
</el-row>
|
||||
</template>
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
title: String,
|
||||
subtitle: String,
|
||||
fontLevel: {
|
||||
type: Number,
|
||||
default: 1
|
||||
}
|
||||
})
|
||||
const fontLevelMap = {
|
||||
1: "24px",
|
||||
2: "18px"
|
||||
}
|
||||
</script>
|
||||
@ -1,19 +1,18 @@
|
||||
<template>
|
||||
<div
|
||||
class="sidebar-logo-container"
|
||||
:class="{ collapse: collapse }"
|
||||
:style="{ backgroundColor: sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground }"
|
||||
>
|
||||
<div class="sidebar-logo-container" :class="{ collapse: collapse }"
|
||||
:style="{ backgroundColor: sideTheme === 'theme-dark' ? variables.menuBackground : variables.menuLightBackground }">
|
||||
<transition :enter-active-class="proxy?.animate.logoAnimate.enter" mode="out-in">
|
||||
<router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
|
||||
<img v-if="logo" :src="logo" class="sidebar-logo" />
|
||||
<h1 v-else class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor }">
|
||||
<h1 v-else class="sidebar-title"
|
||||
:style="{ color: sideTheme === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor }">
|
||||
{{ title }}
|
||||
</h1>
|
||||
</router-link>
|
||||
<router-link v-else key="expand" class="sidebar-logo-link" to="/">
|
||||
<img v-if="logo" :src="logo" class="sidebar-logo" />
|
||||
<h1 class="sidebar-title" :style="{ color: sideTheme === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor }">
|
||||
<h1 class="sidebar-title"
|
||||
:style="{ color: sideTheme === 'theme-dark' ? variables.logoTitleColor : variables.logoLightTitleColor }">
|
||||
{{ title }}
|
||||
</h1>
|
||||
</router-link>
|
||||
@ -34,7 +33,7 @@ defineProps({
|
||||
}
|
||||
});
|
||||
|
||||
const title = ref('RuoYi-Vue-Plus');
|
||||
const title = ref('新能源场站智慧运维平台');
|
||||
const settingsStore = useSettingsStore();
|
||||
const sideTheme = computed(() => settingsStore.sideTheme);
|
||||
</script>
|
||||
|
||||
@ -34,6 +34,8 @@ import i18n from '@/lang/index';
|
||||
// vxeTable
|
||||
import VXETable from 'vxe-table';
|
||||
import 'vxe-table/lib/style.css';
|
||||
import '@/assets/fonts/fonts.scss';
|
||||
|
||||
VXETable.setConfig({
|
||||
zIndex: 999999
|
||||
});
|
||||
|
||||
497
src/views/pvSystem/alarmAnalysis/components/data.vue
Normal file
@ -0,0 +1,497 @@
|
||||
<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 :span="6">
|
||||
<div class="metric-card">
|
||||
<div class="metric-value">{{ props.dashboardData.todayAlarmTotal }}</div>
|
||||
<div class="metric-label">今日报警总数</div>
|
||||
<div class="metric-change">较上周 <span :class="props.dashboardData.updates.todayAlarmTotal.type">
|
||||
{{ props.dashboardData.updates.todayAlarmTotal.type === 'up' ? '↑' : '↓' }}{{ props.dashboardData.updates.todayAlarmTotal.value }}
|
||||
</span></div>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
|
||||
<el-col :span="6">
|
||||
<div class="metric-card">
|
||||
<div class="metric-value">{{ props.dashboardData.unhandledAlarms }}</div>
|
||||
<div class="metric-label">未处理报警</div>
|
||||
<div class="metric-change">较上周 <span :class="props.dashboardData.updates.unhandledAlarms.type">
|
||||
{{ props.dashboardData.updates.unhandledAlarms.type === 'up' ? '↑' : '↓' }}{{ props.dashboardData.updates.unhandledAlarms.value }}
|
||||
</span></div>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
|
||||
<el-col :span="6">
|
||||
<div class="metric-card">
|
||||
<div class="metric-value">{{ props.dashboardData.handledAlarms }}</div>
|
||||
<div class="metric-label">已处理报警</div>
|
||||
<div class="metric-change">较上周 <span :class="props.dashboardData.updates.handledAlarms.type">
|
||||
{{ props.dashboardData.updates.handledAlarms.type === 'up' ? '↑' : '↓' }}{{ props.dashboardData.updates.handledAlarms.value }}
|
||||
</span></div>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
|
||||
<el-col :span="6">
|
||||
<div class="metric-card">
|
||||
<div class="metric-value">{{ props.dashboardData.avgProcessTime }}</div>
|
||||
<div class="metric-label">平均处理时长</div>
|
||||
<div class="metric-change">较上周 <span :class="props.dashboardData.updates.avgProcessTime.type">
|
||||
{{ props.dashboardData.updates.avgProcessTime.type === 'up' ? '↑' : '↓' }}{{ props.dashboardData.updates.avgProcessTime.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 :class="props.chartData.dailyChanges.alarmCount.type">
|
||||
{{ props.chartData.dailyChanges.alarmCount.type === 'up' ? '↑' : '↓' }}{{ props.chartData.dailyChanges.alarmCount.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 :class="props.chartData.dailyChanges.processEfficiency.type">
|
||||
{{ props.chartData.dailyChanges.processEfficiency.type === 'up' ? '↑' : '↓' }}{{ 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: 'down' },
|
||||
processEfficiency: { value: '0.9%', type: 'down' }
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
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: '-45px',
|
||||
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: '-45px',
|
||||
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: #ff4d4f;
|
||||
}
|
||||
|
||||
.down {
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.chart-content {
|
||||
flex: 1;
|
||||
height: 110px;
|
||||
}
|
||||
|
||||
// Element Plus 样式覆盖
|
||||
:deep(.el-select) {
|
||||
width: 80px;
|
||||
}
|
||||
</style>
|
||||
145
src/views/pvSystem/alarmAnalysis/components/pie.vue
Normal file
@ -0,0 +1,145 @@
|
||||
<template>
|
||||
<div class="pie-chart-container">
|
||||
<div class="chart-header">
|
||||
<TitleComponent title="报警类型分布" :fontLevel="2" />
|
||||
<el-select v-model="selectedTimeRange" placeholder="选择时间范围" size="small">
|
||||
<el-option label="今日" value="today" />
|
||||
</el-select>
|
||||
</div>
|
||||
<div ref="pieChartRef" class="chart-content"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, watch } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
import TitleComponent from '@/components/TitleComponent/index.vue';
|
||||
|
||||
// 定义props
|
||||
const props = defineProps({
|
||||
alarmTypeData: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
});
|
||||
|
||||
const selectedTimeRange = ref('today');
|
||||
const pieChartRef = ref(null);
|
||||
let chartInstance = null;
|
||||
|
||||
onMounted(() => {
|
||||
initChart();
|
||||
window.addEventListener('resize', handleResize);
|
||||
});
|
||||
|
||||
const handleResize = () => {
|
||||
if (chartInstance) {
|
||||
chartInstance.resize();
|
||||
}
|
||||
};
|
||||
|
||||
const initChart = () => {
|
||||
if (pieChartRef.value) {
|
||||
chartInstance = echarts.init(pieChartRef.value);
|
||||
} else {
|
||||
console.warn('Chart container not found');
|
||||
return;
|
||||
}
|
||||
|
||||
const option = {
|
||||
backgroundColor: '#fff',
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{b}: {d}%'
|
||||
},
|
||||
legend: {
|
||||
bottom: '5%',
|
||||
left: 'center',
|
||||
formatter: (name) => {
|
||||
// 计算每个数据项的百分比
|
||||
const data = option.series[0].data;
|
||||
const total = data.reduce((sum, item) => sum + item.value, 0);
|
||||
const item = data.find(item => item.name === name);
|
||||
const percentage = item ? Math.round((item.value / total) * 100) : 0;
|
||||
return `${name}(${percentage}%)`;
|
||||
}
|
||||
},
|
||||
// 添加图形组件,用于在半圆中心显示图片
|
||||
graphic: [
|
||||
{
|
||||
type: 'image',
|
||||
id: 'alarmImage',
|
||||
// 图片位置与半圆中心一致
|
||||
left: 'center',
|
||||
top: '50%',
|
||||
// 图片大小
|
||||
width: 60,
|
||||
height: 60,
|
||||
// 图片路径
|
||||
style: {
|
||||
image: '/src/assets/demo/baojing.png',
|
||||
// 保持图片原始比例
|
||||
width: 60,
|
||||
height: 60
|
||||
},
|
||||
// 图片中心点调整
|
||||
origin: [30, 30]
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: '报警类型分布',
|
||||
type: 'pie',
|
||||
radius: [100, 200],
|
||||
center: ['50%', '70%'],
|
||||
// adjust the start and end angle
|
||||
startAngle: 180,
|
||||
endAngle: 360,
|
||||
data: props.alarmTypeData,
|
||||
label: {
|
||||
show: true,
|
||||
position: 'inner',
|
||||
formatter: '{d}%',
|
||||
color: '#fff',
|
||||
fontSize: 14,
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
chartInstance.setOption(option);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.pie-chart-container {
|
||||
width: 100%;
|
||||
height: 400px;
|
||||
background: #fff;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.chart-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.chart-header h3 {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.chart-content {
|
||||
width: 100%;
|
||||
height: calc(100% - 60px);
|
||||
}
|
||||
|
||||
:deep(.el-select) {
|
||||
width: 80px;
|
||||
}
|
||||
</style>
|
||||
226
src/views/pvSystem/alarmAnalysis/components/status.vue
Normal file
@ -0,0 +1,226 @@
|
||||
<template>
|
||||
<el-form :inline="true" :model="formInline" style="display: flex; width: 100%;">
|
||||
<div style="display: flex; gap: 16px;">
|
||||
<el-form-item label="状态:">
|
||||
<el-select v-model="formInline.status" placeholder="状态" clearable style="width: 120px;">
|
||||
<el-option v-for="(item, key) in statusMap" :key="key" :label="item.label" :value="key" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="级别:">
|
||||
<el-select v-model="formInline.alarmLevel" placeholder="级别" clearable style="width: 120px;">
|
||||
<el-option v-for="(item, key) in alarmLevelMap" :key="key" :label="item.label" :value="key" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="类别:">
|
||||
<el-select v-model="formInline.alarmType" placeholder="类别" clearable style="width: 120px;">
|
||||
<el-option v-for="item in alarmTypeList" :key="item" :label="item" :value="item" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="default" @click="clearFilters">清除筛选</el-button>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div style="margin-left: auto;">
|
||||
<el-form-item label="排序:">
|
||||
<el-select v-model="formInline.region" placeholder="排序" clearable style="width: 120px;">
|
||||
<el-option label="Zone one" value="shanghai" />
|
||||
<el-option label="Zone two" value="beijing" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form>
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="listData"
|
||||
border
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-table-column label="报警时间" align="center" prop="time" />
|
||||
<el-table-column label="设备名称" align="center" prop="toolname" />
|
||||
<el-table-column label="报警类型" align="center" prop="alarmType" />
|
||||
<el-table-column label="报警级别" align="center" prop="alarmLevel">
|
||||
<template #default="scope">
|
||||
<el-tag :type="alarmLevelMap[scope.row.alarmLevel].type">{{
|
||||
alarmLevelMap[scope.row.alarmLevel].label }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" align="center" prop="status">
|
||||
<template #default="scope">
|
||||
<el-tag :type="statusMap[scope.row.status].type">{{ statusMap[scope.row.status].label }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="负责人" align="center" prop="manager" />
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="200">
|
||||
<template #default="scope">
|
||||
<div style="display: flex;align-items: center;">
|
||||
<el-button link type="primary" :icon="View">查看</el-button>
|
||||
<div v-if="scope.row.status == 1"
|
||||
style="color: #43CF7C;margin-left: 5px; display: flex; align-items: center;">
|
||||
<el-icon style="margin-right: 4px; font-size: 14px;">
|
||||
<CircleCheckFilled />
|
||||
</el-icon>
|
||||
<span style="font-size: 13px;">已处理</span>
|
||||
</div>
|
||||
<el-button link type="success" v-else :icon="Opportunity">处理</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<div class="pagination-wrapper">
|
||||
<div class="pagination-info">
|
||||
显示第 {{ (currentPage - 1) * pageSize + 1 }} 到第
|
||||
{{ Math.min(currentPage * pageSize, total) }} 条,共 {{ total }} 条记录
|
||||
</div>
|
||||
<el-pagination
|
||||
v-model:current-page="currentPage"
|
||||
v-model:page-size="pageSize"
|
||||
:page-sizes="[5, 10, 20, 50, 100]"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="total"
|
||||
background
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { View, CircleCheckFilled, Opportunity } from '@element-plus/icons-vue'
|
||||
import { ref, computed, reactive } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
// 定义props
|
||||
const props = defineProps({
|
||||
alarmRecords: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
});
|
||||
|
||||
// 分页数据 - 设置为第1页,一页显示5条
|
||||
const currentPage = ref(1);
|
||||
const pageSize = ref(5);
|
||||
const total = ref(props.alarmRecords.length); // 使用传入的数据长度
|
||||
const formInline = reactive({})
|
||||
const loading = ref(false);
|
||||
|
||||
// 使用props中的数据而不是本地虚拟数据
|
||||
const fullData = props.alarmRecords;
|
||||
|
||||
// 状态映射
|
||||
const statusMap = {
|
||||
1: {
|
||||
label: "已处理",
|
||||
type: "success"
|
||||
},
|
||||
2: {
|
||||
label: "未处理",
|
||||
type: "danger"
|
||||
},
|
||||
3: {
|
||||
label: "处理中",
|
||||
type: "primary"
|
||||
}
|
||||
}
|
||||
|
||||
// 报警级别映射
|
||||
const alarmLevelMap = {
|
||||
1: {
|
||||
label: "紧急",
|
||||
type: "danger"
|
||||
},
|
||||
2: {
|
||||
label: "重要",
|
||||
type: "warning"
|
||||
},
|
||||
3: {
|
||||
label: "一般",
|
||||
type: "info"
|
||||
}
|
||||
}
|
||||
|
||||
// 报警类型列表
|
||||
const alarmTypeList = computed(() => {
|
||||
const types = [...new Set(fullData.map(item => item.alarmType))];
|
||||
return types;
|
||||
});
|
||||
|
||||
// 计算当前页显示的数据(包含筛选功能)
|
||||
const listData = computed(() => {
|
||||
// 先进行筛选
|
||||
let filteredData = fullData.filter(item => {
|
||||
// 状态筛选
|
||||
if (formInline.status !== undefined && formInline.status !== null && formInline.status !== '') {
|
||||
if (item.status !== parseInt(formInline.status)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// 级别筛选
|
||||
if (formInline.alarmLevel !== undefined && formInline.alarmLevel !== null && formInline.alarmLevel !== '') {
|
||||
if (item.alarmLevel !== parseInt(formInline.alarmLevel)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// 类别筛选
|
||||
if (formInline.alarmType !== undefined && formInline.alarmType !== null && formInline.alarmType !== '') {
|
||||
if (item.alarmType !== formInline.alarmType) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
// 更新总条数
|
||||
total.value = filteredData.length;
|
||||
|
||||
// 然后进行分页
|
||||
const startIndex = (currentPage.value - 1) * pageSize.value;
|
||||
const endIndex = startIndex + pageSize.value;
|
||||
return filteredData.slice(startIndex, endIndex);
|
||||
});
|
||||
|
||||
// 清除筛选条件
|
||||
const clearFilters = () => {
|
||||
// 清除所有筛选条件
|
||||
Object.keys(formInline).forEach(key => {
|
||||
if (key !== 'region') { // 保留排序功能
|
||||
delete formInline[key];
|
||||
}
|
||||
});
|
||||
// 重置到第一页
|
||||
currentPage.value = 1;
|
||||
// 显示提示信息
|
||||
ElMessage.success('筛选条件已清除');
|
||||
};
|
||||
// 分页处理函数
|
||||
const handleSizeChange = (size) => {
|
||||
pageSize.value = size;
|
||||
currentPage.value = 1; // 切换每页显示条数时重置到第一页
|
||||
// 模拟数据加载效果
|
||||
loading.value = true;
|
||||
setTimeout(() => {
|
||||
loading.value = false;
|
||||
}, 300);
|
||||
};
|
||||
|
||||
const handleCurrentChange = (current) => {
|
||||
currentPage.value = current;
|
||||
// 模拟数据加载效果
|
||||
loading.value = true;
|
||||
setTimeout(() => {
|
||||
loading.value = false;
|
||||
}, 300);
|
||||
};
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.pagination-wrapper {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-top: 10px;
|
||||
border-top: 1px solid #f5f5f5;
|
||||
}
|
||||
|
||||
.pagination-info {
|
||||
color: #8c8c8c;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
161
src/views/pvSystem/alarmAnalysis/index.vue
Normal file
@ -0,0 +1,161 @@
|
||||
<template>
|
||||
<div class="model">
|
||||
<!-- 标题栏 -->
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="12">
|
||||
<TitleComponent title="报警分析与管理" subtitle="实时监控光伏报警信息,高效处理运维任务" />
|
||||
</el-col>
|
||||
<!-- 外层col:控制整体宽度并右对齐,同时作为flex容器 -->
|
||||
<el-col :span="12" style="display: flex; justify-content: flex-end; align-items: center;">
|
||||
<el-col :span="7">
|
||||
<el-input placeholder="搜索报警信息">
|
||||
<template #prefix>
|
||||
<el-icon>
|
||||
<Search />
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<el-button type="primary">
|
||||
导出数据
|
||||
<el-icon class="el-icon--right">
|
||||
<UploadFilled />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
</el-col>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!-- 数据展示-->
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="16">
|
||||
<el-card style="height: 100%;">
|
||||
<div style="height: 100%;">
|
||||
<Data :dashboardData="dashboardData" :chartData="chartData" />
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-card style="height: 100%;">
|
||||
<div style="height: 100%;">
|
||||
<Pie :alarmTypeData="alarmTypeData" />
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!-- 状态表 -->
|
||||
<el-row style="margin-top: 20px;">
|
||||
<el-col :span="24">
|
||||
<el-card>
|
||||
<TitleComponent title="报警记录" :fontLevel="2" />
|
||||
<status style="width: 100%;" :alarmRecords="alarmRecords" />
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import TitleComponent from '@/components/TitleComponent/index.vue';
|
||||
import Status from '@/views/pvSystem/alarmAnalysis/components/status.vue'
|
||||
import Pie from '@/views/pvSystem/alarmAnalysis/components/pie.vue';
|
||||
import Data from '@/views/pvSystem/alarmAnalysis/components/data.vue';
|
||||
|
||||
// Mock数据
|
||||
const dashboardData = ref({
|
||||
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' }
|
||||
}
|
||||
});
|
||||
|
||||
const chartData = ref({
|
||||
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: 'down' },
|
||||
processEfficiency: { value: '0.9%', type: 'down' }
|
||||
}
|
||||
});
|
||||
|
||||
const alarmTypeData = ref([
|
||||
{ value: 25, name: '逆变器故障', itemStyle: { color: '#F94144' } },
|
||||
{ value: 20, name: '组串异常', itemStyle: { color: '#F3722C' } },
|
||||
{ value: 15, name: '通讯中断', itemStyle: { color: '#00BAAD' } },
|
||||
{ value: 15, name: '汇流箱报警', itemStyle: { color: '#186DF5' } }
|
||||
]);
|
||||
|
||||
const alarmRecords = ref([
|
||||
{ time: "2025-10-15 08:30:22", toolname: '逆变器 INV-001', alarmType: "逆变器过温保护", alarmLevel: 1, status: 1, manager: "张工程师" },
|
||||
{ time: "2025-10-15 09:15:45", toolname: '逆变器 INV-001', alarmType: "逆变器过温保护", alarmLevel: 2, status: 1, manager: "李工程师" },
|
||||
{ time: "2025-10-15 10:42:18", toolname: '逆变器 INV-001', alarmType: "逆变器过温保护", alarmLevel: 3, status: 2, manager: "王工程师" },
|
||||
{ time: "2025-10-15 11:20:33", toolname: '逆变器 INV-001', alarmType: "逆变器过温保护", alarmLevel: 1, status: 2, manager: "赵工程师" },
|
||||
{ time: "2025-10-15 13:55:09", toolname: '逆变器 INV-001', alarmType: "逆变器过温保护", alarmLevel: 2, status: 3, manager: "孙工程师" },
|
||||
{ time: "2025-10-15 14:30:56", toolname: '逆变器 INV-002', alarmType: "通讯中断", alarmLevel: 1, status: 3, manager: "周工程师" },
|
||||
{ time: "2025-10-15 15:20:12", toolname: '逆变器 INV-002', alarmType: "通讯中断", alarmLevel: 2, status: 3, manager: "吴工程师" },
|
||||
{ time: "2025-10-15 16:05:47", toolname: '逆变器 INV-002', alarmType: "通讯中断", alarmLevel: 1, status: 3, manager: "郑工程师" },
|
||||
{ time: "2025-10-15 17:30:29", toolname: '逆变器 INV-002', alarmType: "通讯中断", alarmLevel: 2, status: 3, manager: "钱工程师" },
|
||||
{ time: "2025-10-15 18:15:54", toolname: '逆变器 INV-002', alarmType: "通讯中断", alarmLevel: 1, status: 3, manager: "冯工程师" },
|
||||
{ time: "2025-10-16 08:30:22", toolname: '汇流箱 HLB-001', alarmType: "组串异常", alarmLevel: 1, status: 1, manager: "陈工程师" },
|
||||
{ time: "2025-10-16 09:15:45", toolname: '汇流箱 HLB-001', alarmType: "组串异常", alarmLevel: 2, status: 1, manager: "卫工程师" },
|
||||
{ time: "2025-10-16 10:42:18", toolname: '汇流箱 HLB-001', alarmType: "组串异常", alarmLevel: 3, status: 2, manager: "蒋工程师" },
|
||||
{ time: "2025-10-16 11:20:33", toolname: '汇流箱 HLB-001', alarmType: "组串异常", alarmLevel: 1, status: 2, manager: "沈工程师" },
|
||||
{ time: "2025-10-16 13:55:09", toolname: '汇流箱 HLB-001', alarmType: "组串异常", alarmLevel: 2, status: 3, manager: "韩工程师" },
|
||||
{ time: "2025-10-16 14:30:56", toolname: '汇流箱 HLB-002', alarmType: "过压保护", alarmLevel: 1, status: 3, manager: "杨工程师" },
|
||||
{ time: "2025-10-16 15:20:12", toolname: '汇流箱 HLB-002', alarmType: "过压保护", alarmLevel: 2, status: 3, manager: "朱工程师" },
|
||||
{ time: "2025-10-16 16:05:47", toolname: '汇流箱 HLB-002', alarmType: "过压保护", alarmLevel: 1, status: 3, manager: "秦工程师" },
|
||||
{ time: "2025-10-16 17:30:29", toolname: '汇流箱 HLB-002', alarmType: "过压保护", alarmLevel: 2, status: 3, manager: "尤工程师" },
|
||||
{ time: "2025-10-16 18:15:54", toolname: '汇流箱 HLB-002', alarmType: "过压保护", alarmLevel: 1, status: 3, manager: "许工程师" },
|
||||
{ time: "2025-10-17 08:30:22", toolname: '配电柜 PD-001', alarmType: "电流异常", alarmLevel: 1, status: 1, manager: "何工程师" },
|
||||
{ time: "2025-10-17 09:15:45", toolname: '配电柜 PD-001', alarmType: "电流异常", alarmLevel: 2, status: 1, manager: "吕工程师" },
|
||||
{ time: "2025-10-17 10:42:18", toolname: '配电柜 PD-001', alarmType: "电流异常", alarmLevel: 3, status: 2, manager: "施工程师" },
|
||||
{ time: "2025-10-17 11:20:33", toolname: '配电柜 PD-001', alarmType: "电流异常", alarmLevel: 1, status: 2, manager: "张工程师" },
|
||||
{ time: "2025-10-17 13:55:09", toolname: '配电柜 PD-001', alarmType: "电流异常", alarmLevel: 2, status: 3, manager: "孔工程师" },
|
||||
{ time: "2025-10-17 14:30:56", toolname: '配电柜 PD-002', alarmType: "电压异常", alarmLevel: 1, status: 3, manager: "曹工程师" },
|
||||
{ time: "2025-10-17 15:20:12", toolname: '配电柜 PD-002', alarmType: "电压异常", alarmLevel: 2, status: 3, manager: "严工程师" },
|
||||
{ time: "2025-10-17 16:05:47", toolname: '配电柜 PD-002', alarmType: "电压异常", alarmLevel: 1, status: 3, manager: "华工程师" },
|
||||
{ time: "2025-10-17 17:30:29", toolname: '配电柜 PD-002', alarmType: "电压异常", alarmLevel: 2, status: 3, manager: "金工程师" },
|
||||
{ time: "2025-10-17 18:15:54", toolname: '配电柜 PD-002', alarmType: "电压异常", alarmLevel: 1, status: 3, manager: "魏工程师" },
|
||||
{ time: "2025-10-18 08:30:22", toolname: '环境监测仪 EM-001', alarmType: "温度过高", alarmLevel: 1, status: 1, manager: "陶工程师" },
|
||||
{ time: "2025-10-18 09:15:45", toolname: '环境监测仪 EM-001', alarmType: "温度过高", alarmLevel: 2, status: 1, manager: "姜工程师" },
|
||||
{ time: "2025-10-18 10:42:18", toolname: '环境监测仪 EM-001', alarmType: "温度过高", alarmLevel: 3, status: 2, manager: "戚工程师" },
|
||||
{ time: "2025-10-18 11:20:33", toolname: '环境监测仪 EM-001', alarmType: "温度过高", alarmLevel: 1, status: 2, manager: "谢工程师" },
|
||||
{ time: "2025-10-18 13:55:09", toolname: '环境监测仪 EM-001', alarmType: "温度过高", alarmLevel: 2, status: 3, manager: "邹工程师" },
|
||||
{ time: "2025-10-18 14:30:56", toolname: '环境监测仪 EM-002', alarmType: "湿度异常", alarmLevel: 1, status: 3, manager: "喻工程师" },
|
||||
{ time: "2025-10-18 15:20:12", toolname: '环境监测仪 EM-002', alarmType: "湿度异常", alarmLevel: 2, status: 3, manager: "柏工程师" },
|
||||
{ time: "2025-10-18 16:05:47", toolname: '环境监测仪 EM-002', alarmType: "湿度异常", alarmLevel: 1, status: 3, manager: "水工程师" }
|
||||
]);
|
||||
|
||||
// 模拟接口调用
|
||||
const fetchAlarmData = async () => {
|
||||
try {
|
||||
// 模拟网络请求延迟
|
||||
await new Promise(resolve => setTimeout(resolve, 800));
|
||||
// 这里可以添加数据处理逻辑
|
||||
console.log('模拟API调用成功,数据已加载');
|
||||
} catch (error) {
|
||||
console.error('模拟API调用失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
fetchAlarmData();
|
||||
});
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.model {
|
||||
padding: 0px 15px;
|
||||
background-color: rgba(242, 248, 252, 1);
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,38 @@
|
||||
<template>
|
||||
<div style="display: flex;align-items: center;">
|
||||
<div class="icon">
|
||||
<img :src="`/assets/${icon}.png`" alt="图片" />
|
||||
</div>
|
||||
<div class="subtitle">
|
||||
{{ subtitle }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
const { subtitle, icon } = defineProps({
|
||||
subtitle: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
icon: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style scoped>
|
||||
.subtitle {
|
||||
font-family: Alibaba-PuHuiTi-Bold;
|
||||
font-size: 18px;
|
||||
font-weight: 400;
|
||||
letter-spacing: 0px;
|
||||
line-height: 21.6px;
|
||||
color: rgba(0, 30, 59, 1);
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 30px;
|
||||
}
|
||||
</style>
|
||||
226
src/views/pvSystem/mainSystemDiagram/components/czzljl.vue
Normal file
@ -0,0 +1,226 @@
|
||||
<template>
|
||||
<div class="operation-log-container">
|
||||
<!-- 页面标题 -->
|
||||
<div class="page-title">
|
||||
<el-icon class="title-icon">
|
||||
<DocumentChecked />
|
||||
</el-icon>
|
||||
<h1>操作指令记录</h1>
|
||||
</div>
|
||||
|
||||
<!-- 搜索和操作区域 -->
|
||||
<div class="search-bar">
|
||||
<el-select v-model="selectedCommandType" placeholder="输入操作指令..." class="search-select" clearable>
|
||||
<el-option v-for="type in commandTypes" :key="type.value" :label="type.label" :value="type.value" />
|
||||
</el-select>
|
||||
|
||||
<el-button type="primary" class="send-btn">
|
||||
<el-icon>
|
||||
<Paperclip />
|
||||
</el-icon>
|
||||
发送
|
||||
</el-button>
|
||||
|
||||
<el-button type="primary" :plain="true" class="export-btn">
|
||||
<el-icon>
|
||||
<Download />
|
||||
</el-icon>
|
||||
导出
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 表格区域 -->
|
||||
<el-table :data="tableData" border stripe class="log-table">
|
||||
<el-table-column type="selection" width="50" />
|
||||
|
||||
<el-table-column prop="commandType" label="指令类型" width="120" />
|
||||
|
||||
<el-table-column prop="operationContent" label="操作内容" min-width="200" />
|
||||
|
||||
<el-table-column prop="device" label="设备" width="100" />
|
||||
|
||||
<el-table-column prop="status" label="状态" width="120" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.status === 'SUCCESS' ? 'success' : 'danger'" effect="light"
|
||||
class="status-tag">
|
||||
<el-icon :class="scope.row.status === 'SUCCESS' ? 'success-icon' : 'error-icon'">
|
||||
<Check v-if="scope.row.status === 'SUCCESS'" />
|
||||
<Close v-else />
|
||||
</el-icon>
|
||||
{{ scope.row.status }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="time" label="时间" width="140" />
|
||||
</el-table>
|
||||
|
||||
<!-- 分页区域 -->
|
||||
<div class="pagination-container">
|
||||
<div class="record-info">
|
||||
显示第1到7条,共54条记录
|
||||
</div>
|
||||
|
||||
<el-pagination v-model:current-page="currentPage" :page-size="pageSize" :page-count="totalPages"
|
||||
layout="prev, pager, next" @current-change="handlePageChange" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import {
|
||||
DocumentChecked,
|
||||
Paperclip,
|
||||
Download,
|
||||
Check,
|
||||
Close
|
||||
} from '@element-plus/icons-vue';
|
||||
|
||||
// 指令类型选项
|
||||
const commandTypes = [
|
||||
{ label: '系统自检', value: 'system_check' },
|
||||
{ label: '闭合馈线开关', value: 'close_feeder_switch' },
|
||||
{ label: '闭合保护开关', value: 'close_protection_switch' },
|
||||
{ label: '断开馈线开关', value: 'open_feeder_switch' },
|
||||
];
|
||||
|
||||
// 选中的指令类型
|
||||
const selectedCommandType = ref('');
|
||||
|
||||
// 表格数据
|
||||
const tableData = ref([
|
||||
{
|
||||
commandType: '系统自检',
|
||||
operationContent: '系统进入正常供电模式.',
|
||||
device: '主系统',
|
||||
status: 'SUCCESS',
|
||||
time: '09:51:24'
|
||||
},
|
||||
{
|
||||
commandType: '闭合馈线开关',
|
||||
operationContent: '馈线开关A已闭合,负载A.',
|
||||
device: '主系统',
|
||||
status: 'SUCCESS',
|
||||
time: '09:41:20'
|
||||
},
|
||||
{
|
||||
commandType: '闭合保护开关',
|
||||
operationContent: '闭合母线开关',
|
||||
device: '主系统',
|
||||
status: 'ERROR',
|
||||
time: '09:21:24'
|
||||
},
|
||||
{
|
||||
commandType: '系统自检',
|
||||
operationContent: '母线电压稳定在110KV',
|
||||
device: '主系统',
|
||||
status: 'SUCCESS',
|
||||
time: '09:11:24'
|
||||
},
|
||||
{
|
||||
commandType: '断开馈线开关',
|
||||
operationContent: '闭合馈线开关',
|
||||
device: '主系统',
|
||||
status: 'SUCCESS',
|
||||
time: '09:02:24'
|
||||
},
|
||||
{
|
||||
commandType: '断开馈线开关',
|
||||
operationContent: '闭合馈线开关',
|
||||
device: '主系统',
|
||||
status: 'SUCCESS',
|
||||
time: '09:02:24'
|
||||
},
|
||||
{
|
||||
commandType: '断开馈线开关',
|
||||
operationContent: '闭合馈线开关',
|
||||
device: '主系统',
|
||||
status: 'SUCCESS',
|
||||
time: '09:02:24'
|
||||
}
|
||||
]);
|
||||
|
||||
// 分页相关
|
||||
const currentPage = ref(3);
|
||||
const pageSize = ref(7);
|
||||
const totalPages = ref(20);
|
||||
|
||||
// 处理页码变化
|
||||
const handlePageChange = (page) => {
|
||||
currentPage.value = page;
|
||||
// 实际应用中这里会根据页码加载对应的数据
|
||||
console.log(`切换到第${page}页`);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.operation-log-container {
|
||||
max-width: 1200px;
|
||||
margin: 20px auto;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.title-icon {
|
||||
margin-right: 8px;
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.search-bar {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-bottom: 20px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.search-select {
|
||||
flex: 0 1 300px;
|
||||
}
|
||||
|
||||
.send-btn,
|
||||
.export-btn {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.log-table {
|
||||
width: 100%;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.status-tag {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.success-icon {
|
||||
color: #67c23a;
|
||||
}
|
||||
|
||||
.error-icon {
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
.pagination-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.record-info {
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.el-pagination {
|
||||
--el-pagination-active-color: #409eff;
|
||||
}
|
||||
</style>
|
||||
349
src/views/pvSystem/mainSystemDiagram/components/dzt.vue
Normal file
@ -0,0 +1,349 @@
|
||||
<template>
|
||||
<el-card style="width: 100%;">
|
||||
<div class="circuit-container">
|
||||
<!-- 状态指示器 -->
|
||||
<div class="status-indicators">
|
||||
<div class="status-item">
|
||||
<span class="status-dot closed"></span>
|
||||
<span>闭合</span>
|
||||
</div>
|
||||
<div class="status-item">
|
||||
<span class="status-dot open"></span>
|
||||
<span>断开</span>
|
||||
</div>
|
||||
<div class="status-item">
|
||||
<span class="status-dot running"></span>
|
||||
<span>运行中</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 控制按钮 -->
|
||||
<div class="control-buttons">
|
||||
<button v-for="(device, index) in devices" :key="index" @click="toggleDevice(device.id)"
|
||||
:class="{ 'btn-active': device.status === 'closed' }">
|
||||
{{ device.name }}: {{ device.statusText }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 电路图 SVG -->
|
||||
<svg class="circuit-svg" viewBox="0 0 800 400" xmlns="http://www.w3.org/2000/svg">
|
||||
<!-- 背景网格 -->
|
||||
<pattern id="grid" width="50" height="50" patternUnits="userSpaceOnUse">
|
||||
<path d="M 50 0 L 0 0 0 50" fill="none" stroke="#f0f0f0" stroke-width="1" />
|
||||
</pattern>
|
||||
<rect width="100%" height="100%" fill="url(#grid)" />
|
||||
|
||||
<!-- 主线路径 -->
|
||||
<!-- 发电站到主开关 -->
|
||||
<line x1="100" y1="200" x2="180" y2="200" stroke="#22c55e" stroke-width="4" />
|
||||
|
||||
<!-- 主开关到变压器 -->
|
||||
<line x1="220" y1="200" x2="280" y2="200" stroke="#22c55e" stroke-width="4" />
|
||||
|
||||
<!-- 变压器到母线开关 -->
|
||||
<line x1="360" y1="200" x2="420" y2="200" stroke="#22c55e" stroke-width="4" />
|
||||
|
||||
<!-- 母线开关到分支点 -->
|
||||
<line x1="460" y1="200" x2="500" y2="200" stroke="#22c55e" stroke-width="4" />
|
||||
|
||||
<!-- 分支点到馈线开关A -->
|
||||
<line x1="500" y1="200" x2="500" y2="100" stroke="#22c55e" stroke-width="4" />
|
||||
<line x1="500" y1="100" x2="580" y2="100" stroke="#22c55e" stroke-width="4" />
|
||||
|
||||
<!-- 分支点到馈线开关B -->
|
||||
<line x1="500" y1="200" x2="500" y2="300" stroke="#ef4444" stroke-width="4" stroke-dasharray="5,5" />
|
||||
<line x1="500" y1="300" x2="580" y2="300" stroke="#ef4444" stroke-width="4" stroke-dasharray="5,5" />
|
||||
|
||||
<!-- 馈线开关B到保护开关 -->
|
||||
<line x1="620" y1="300" x2="700" y2="300" stroke="#ef4444" stroke-width="4" stroke-dasharray="5,5" />
|
||||
|
||||
<!-- 发电站 -->
|
||||
<circle cx="100" cy="200" r="30" fill="#3b82f6" stroke="#1e40af" stroke-width="2" />
|
||||
<text x="100" y="205" text-anchor="middle" fill="white" font-weight="bold">发电站</text>
|
||||
|
||||
<!-- 主开关 -->
|
||||
<g :class="{ 'switch-closed': devices[0].status === 'closed' }">
|
||||
<circle cx="200" cy="200" r="15" :fill="devices[0].status === 'closed' ? '#10b981' : '#ef4444'" />
|
||||
<text x="200" y="250" text-anchor="middle" font-size="12">主开关</text>
|
||||
<text x="200" y="270" text-anchor="middle" font-size="10" fill="#666">220kV</text>
|
||||
<text x="200" y="285" text-anchor="middle" font-size="10"
|
||||
:fill="devices[0].status === 'closed' ? '#10b981' : '#ef4444'">
|
||||
{{ devices[0].statusText }}
|
||||
</text>
|
||||
</g>
|
||||
|
||||
<!-- 变压器 -->
|
||||
<g>
|
||||
<rect x="300" y="170" width="60" height="60" fill="white" stroke="#666" stroke-width="2" />
|
||||
<line x1="315" y1="190" x2="345" y2="190" stroke="#666" stroke-width="2" />
|
||||
<line x1="315" y1="210" x2="345" y2="210" stroke="#666" stroke-width="2" />
|
||||
<line x1="315" y1="230" x2="345" y2="230" stroke="#666" stroke-width="2" />
|
||||
<text x="330" y="285" text-anchor="middle" font-size="10" fill="#10b981">已完成</text>
|
||||
</g>
|
||||
|
||||
<!-- 母线开关 -->
|
||||
<g :class="{ 'switch-closed': devices[1].status === 'closed' }">
|
||||
<circle cx="440" cy="200" r="15" :fill="devices[1].status === 'closed' ? '#10b981' : '#ef4444'" />
|
||||
<text x="440" y="250" text-anchor="middle" font-size="12">母线开关</text>
|
||||
<text x="440" y="270" text-anchor="middle" font-size="10" fill="#666">110kV</text>
|
||||
<text x="440" y="285" text-anchor="middle" font-size="10"
|
||||
:fill="devices[1].status === 'closed' ? '#10b981' : '#ef4444'">
|
||||
{{ devices[1].statusText }}
|
||||
</text>
|
||||
</g>
|
||||
|
||||
<!-- 分支点 -->
|
||||
<circle cx="500" cy="200" r="8" fill="#666" />
|
||||
|
||||
<!-- 馈线开关A -->
|
||||
<g :class="{ 'switch-closed': devices[2].status === 'closed' }">
|
||||
<circle cx="600" cy="100" r="15" :fill="devices[2].status === 'closed' ? '#10b981' : '#ef4444'" />
|
||||
<text x="600" y="60" text-anchor="middle" font-size="12">馈线开关A</text>
|
||||
<text x="600" y="80" text-anchor="middle" font-size="10"
|
||||
:fill="devices[2].status === 'closed' ? '#10b981' : '#ef4444'">
|
||||
{{ devices[2].statusText }}
|
||||
</text>
|
||||
</g>
|
||||
|
||||
<!-- 负载A -->
|
||||
<circle cx="680" cy="100" r="20" fill="#3b82f6" stroke="#1e40af" stroke-width="2" />
|
||||
<text x="680" y="105" text-anchor="middle" fill="white" font-size="12">负载A</text>
|
||||
<text x="680" y="135" text-anchor="middle" font-size="10" fill="#10b981">已完成</text>
|
||||
|
||||
<!-- 馈线开关B -->
|
||||
<g :class="{ 'switch-closed': devices[3].status === 'closed' }">
|
||||
<circle cx="600" cy="300" r="15" :fill="devices[3].status === 'closed' ? '#10b981' : '#ef4444'" />
|
||||
<text x="600" y="340" text-anchor="middle" font-size="12">馈线开关B</text>
|
||||
<text x="600" y="360" text-anchor="middle" font-size="10"
|
||||
:fill="devices[3].status === 'closed' ? '#10b981' : '#ef4444'">
|
||||
{{ devices[3].statusText }}
|
||||
</text>
|
||||
</g>
|
||||
|
||||
<!-- 保护开关 -->
|
||||
<g :class="{ 'switch-closed': devices[4].status === 'closed' }">
|
||||
<circle cx="720" cy="300" r="15" :fill="devices[4].status === 'closed' ? '#10b981' : '#ef4444'" />
|
||||
<text x="720" y="340" text-anchor="middle" font-size="12">保护开关</text>
|
||||
<text x="720" y="360" text-anchor="middle" font-size="10"
|
||||
:fill="devices[4].status === 'closed' ? '#10b981' : '#ef4444'">
|
||||
{{ devices[4].statusText }}
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
<!-- WebSocket 连接状态 -->
|
||||
<div class="connection-status">
|
||||
<span>WebSocket 连接状态:</span>
|
||||
<span :class="wsConnected ? 'connected' : 'disconnected'">
|
||||
{{ wsConnected ? '已连接' : '未连接' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted } from 'vue';
|
||||
|
||||
// 设备状态数据
|
||||
const devices = ref([
|
||||
{ id: 'main-switch', name: '主开关', status: 'closed', statusText: '已完成' },
|
||||
{ id: 'bus-switch', name: '母线开关', status: 'closed', statusText: '已完成' },
|
||||
{ id: 'feeder-a', name: '馈线开关A', status: 'closed', statusText: '已完成' },
|
||||
{ id: 'feeder-b', name: '馈线开关B', status: 'open', statusText: '未完成' },
|
||||
{ id: 'protection-switch', name: '保护开关', status: 'open', statusText: '未完成' }
|
||||
]);
|
||||
|
||||
// WebSocket 连接状态
|
||||
const wsConnected = ref(false);
|
||||
let ws = null;
|
||||
|
||||
// 切换设备状态
|
||||
const toggleDevice = (deviceId) => {
|
||||
const device = devices.value.find(d => d.id === deviceId);
|
||||
if (device) {
|
||||
// 切换状态
|
||||
device.status = device.status === 'closed' ? 'open' : 'closed';
|
||||
device.statusText = device.status === 'closed' ? '已完成' : '未完成';
|
||||
|
||||
// 发送状态到服务器
|
||||
if (ws && ws.readyState === WebSocket.OPEN) {
|
||||
ws.send(JSON.stringify({
|
||||
type: 'status-update',
|
||||
deviceId,
|
||||
status: device.status
|
||||
}));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 连接 WebSocket
|
||||
const connectWebSocket = () => {
|
||||
// 实际应用中替换为真实的 WebSocket 服务器地址
|
||||
const wsUrl = 'ws://localhost:8080/ws/circuit';
|
||||
|
||||
// 关闭现有连接
|
||||
if (ws) {
|
||||
ws.close();
|
||||
}
|
||||
|
||||
// 创建新连接
|
||||
ws = new WebSocket(wsUrl);
|
||||
|
||||
ws.onopen = () => {
|
||||
console.log('WebSocket 连接已建立');
|
||||
wsConnected.value = true;
|
||||
|
||||
// 发送当前状态
|
||||
ws.send(JSON.stringify({
|
||||
type: 'initial-status',
|
||||
status: devices.value.reduce((acc, device) => {
|
||||
acc[device.id] = device.status;
|
||||
return acc;
|
||||
}, {})
|
||||
}));
|
||||
};
|
||||
|
||||
ws.onmessage = (event) => {
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
console.log('收到消息:', data);
|
||||
|
||||
// 处理状态更新
|
||||
if (data.type === 'status-update' && data.deviceId && data.status) {
|
||||
const device = devices.value.find(d => d.id === data.deviceId);
|
||||
if (device) {
|
||||
device.status = data.status;
|
||||
device.statusText = data.status === 'closed' ? '已完成' : '未完成';
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('解析 WebSocket 消息失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
ws.onclose = () => {
|
||||
console.log('WebSocket 连接已关闭');
|
||||
wsConnected.value = false;
|
||||
|
||||
// 5秒后尝试重连
|
||||
setTimeout(connectWebSocket, 5000);
|
||||
};
|
||||
|
||||
ws.onerror = (error) => {
|
||||
console.error('WebSocket 错误:', error);
|
||||
};
|
||||
};
|
||||
|
||||
// 组件挂载时连接 WebSocket
|
||||
onMounted(() => {
|
||||
connectWebSocket();
|
||||
});
|
||||
|
||||
// 组件卸载时关闭 WebSocket 连接
|
||||
onUnmounted(() => {
|
||||
if (ws) {
|
||||
ws.close();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.circuit-container {
|
||||
font-family: Arial, sans-serif;
|
||||
padding: 20px;
|
||||
max-width: 1000px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.status-indicators {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
margin-bottom: 15px;
|
||||
padding: 10px;
|
||||
background-color: #f9f9f9;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.status-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.status-dot {
|
||||
display: inline-block;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.status-dot.closed {
|
||||
background-color: #10b981;
|
||||
}
|
||||
|
||||
.status-dot.open {
|
||||
background-color: #ef4444;
|
||||
}
|
||||
|
||||
.status-dot.running {
|
||||
background-color: #eab308;
|
||||
}
|
||||
|
||||
.control-buttons {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.control-buttons button {
|
||||
padding: 8px 16px;
|
||||
background-color: #f0f0f0;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.control-buttons button.btn-active {
|
||||
background-color: #10b981;
|
||||
color: white;
|
||||
border-color: #059669;
|
||||
}
|
||||
|
||||
.control-buttons button:hover {
|
||||
background-color: #e0e0e0;
|
||||
}
|
||||
|
||||
.control-buttons button.btn-active:hover {
|
||||
background-color: #059669;
|
||||
}
|
||||
|
||||
.circuit-svg {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.connection-status {
|
||||
margin-top: 15px;
|
||||
padding: 10px;
|
||||
background-color: #f9f9f9;
|
||||
border-radius: 5px;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.connection-status .connected {
|
||||
color: #10b981;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.connection-status .disconnected {
|
||||
color: #ef4444;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
69
src/views/pvSystem/mainSystemDiagram/components/glsc.vue
Normal file
@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<div>
|
||||
<RightTitle subtitle="功率输出趋势" icon="glsc" />
|
||||
</div>
|
||||
<div ref="chartRef" style="width: 100%; height: 400px;"></div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import * as echarts from 'echarts';
|
||||
import { ref, onMounted } from 'vue';
|
||||
import RightTitle from './RightTitle.vue';
|
||||
// 创建对 chart DOM 的引用
|
||||
const chartRef = ref(null);
|
||||
|
||||
// 初始化图表并设置配置
|
||||
onMounted(() => {
|
||||
const chart = echarts.init(chartRef.value); // 初始化图表
|
||||
|
||||
const option = {
|
||||
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
},
|
||||
legend: {
|
||||
data: ['发电机1', '发电机2'],
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10'],
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
min: 10,
|
||||
max: 20,
|
||||
interval: 2,
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '发电机1',
|
||||
type: 'line',
|
||||
data: [14, 16, 14, 15, 16, 14, 16, 15, 16, 17, 16],
|
||||
smooth: true,
|
||||
color: '#3366FF',
|
||||
areaStyle: {
|
||||
opacity: 0.2,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '发电机2',
|
||||
type: 'line',
|
||||
data: [16, 17, 15, 16, 18, 16, 17, 17, 18, 19, 18],
|
||||
smooth: true,
|
||||
color: '#FFCC00',
|
||||
areaStyle: {
|
||||
opacity: 0.2,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// 设置图表的配置
|
||||
chart.setOption(option);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* Optional styling */
|
||||
</style>
|
||||
217
src/views/pvSystem/mainSystemDiagram/components/sbqk.vue
Normal file
@ -0,0 +1,217 @@
|
||||
<template>
|
||||
<el-card style="width: 100%;">
|
||||
<div class="container">
|
||||
<div class="item">
|
||||
<div class="top">
|
||||
<div class="icon">
|
||||
<img src="/assets/sb1.png" alt="">
|
||||
</div>
|
||||
<div class="content">
|
||||
<p class="t1">运行中</p>
|
||||
<p class="t2">当前状态</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bottom">
|
||||
<span class="icon"><svg xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink" width="17.4462890625" height="4.361663818359375"
|
||||
viewBox="0 0 17.4462890625 4.361663818359375" fill="none">
|
||||
<path
|
||||
d="M14.43 2.05L14.43 0.4C14.43 0.32495 14.5049 0.25 14.58 0.25L14.66 0.25L17.14 2.05C17.2151 2.12505 17.2151 2.20495 17.14 2.28L14.66 4.08C14.585 4.15505 14.505 4.08 14.43 4.08L14.43 2.5L0.55 2.5C0.399897 2.6501 0.25 2.4301 0.25 2.28C0.25 2.12988 0.399897 1.98 0.55 1.98L14.43 1.98L14.43 2.05Z"
|
||||
stroke="rgba(67, 207, 124, 1)" stroke-width="0.5" fill="#43CF7C">
|
||||
</path>
|
||||
</svg>
|
||||
</span>
|
||||
<span class="t1">平稳运行</span>
|
||||
<span class="t2">(30 days)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="top">
|
||||
<div class="icon">
|
||||
<img src="/assets/sb2.png" alt="">
|
||||
</div>
|
||||
<div class="content">
|
||||
<p class="t1" style="font-size: 21px;">128小时</p>
|
||||
<p class="t2">运行时长</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bottom">
|
||||
<span class="icon"><svg xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink" width="17.4462890625" height="4.361663818359375"
|
||||
viewBox="0 0 17.4462890625 4.361663818359375" fill="none">
|
||||
<path
|
||||
d="M14.43 2.05L14.43 0.4C14.43 0.32495 14.5049 0.25 14.58 0.25L14.66 0.25L17.14 2.05C17.2151 2.12505 17.2151 2.20495 17.14 2.28L14.66 4.08C14.585 4.15505 14.505 4.08 14.43 4.08L14.43 2.5L0.55 2.5C0.399897 2.6501 0.25 2.4301 0.25 2.28C0.25 2.12988 0.399897 1.98 0.55 1.98L14.43 1.98L14.43 2.05Z"
|
||||
stroke="rgba(67, 207, 124, 1)" stroke-width="0.5" fill="#43CF7C">
|
||||
</path>
|
||||
</svg>
|
||||
</span>
|
||||
<span class="t1">平稳运行</span>
|
||||
<span class="t2">(30 days)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="top">
|
||||
<div class="icon">
|
||||
<img src="/assets/sb3.png" alt="">
|
||||
</div>
|
||||
<div class="content">
|
||||
<p class="t1">5/8</p>
|
||||
<p class="t2">已完成步骤</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bottom">
|
||||
<span class="icon"><svg xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink" width="12.393798828125" height="7.42498779296875"
|
||||
viewBox="0 0 12.393798828125 7.42498779296875" fill="none">
|
||||
<path
|
||||
d="M12.2438 0.225L12.2438 0.150012L12.2438 0.0750058L12.1688 0.0750058C12.1688 0.0750058 12.0937 0.0750058 12.0188 0L8.34376 0C7.96876 0 7.66876 0.300006 7.66876 0.675C7.66876 1.05001 7.96876 1.35 8.34376 1.35L10.2188 1.35L6.61876 4.95L4.81876 3.15C4.44375 2.775 3.69376 2.775 3.31875 3.15L0.16875 6.3C-0.05625 6.525 -0.05625 6.975 0.16875 7.2C0.318762 7.35001 0.468756 7.425 0.61875 7.425C0.768762 7.425 0.918756 7.425 1.06875 7.2L3.99375 4.275L5.79375 6.075C6.16876 6.45001 6.91875 6.45001 7.29376 6.075L11.0438 2.32501L11.0438 4.20001C11.0438 4.575 11.3438 4.87501 11.7188 4.87501C12.0937 4.87501 12.3938 4.575 12.3938 4.20001L12.3938 0.825011L12.3938 0.600011L12.2438 0.225Z"
|
||||
fill="#00B87A">
|
||||
</path>
|
||||
</svg>
|
||||
|
||||
</span>
|
||||
<span class="t1">完成率98%</span>
|
||||
<span class="t2">(30 days)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="top">
|
||||
<div class="icon">
|
||||
<img src="/assets/sb4.png" alt="">
|
||||
</div>
|
||||
<div class="content">
|
||||
<p class="t1" style="color: rgba(255, 153, 0, 1);">2条</p>
|
||||
<p class="t2">告警信息</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bottom">
|
||||
<span class="icon"><svg xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink" width="12.393798828125" height="7.42498779296875"
|
||||
viewBox="0 0 12.393798828125 7.42498779296875" fill="none">
|
||||
<path
|
||||
d="M12.2438 0.225L12.2438 0.150012L12.2438 0.0750058L12.1688 0.0750058C12.1688 0.0750058 12.0937 0.0750058 12.0188 0L8.34376 0C7.96876 0 7.66876 0.300006 7.66876 0.675C7.66876 1.05001 7.96876 1.35 8.34376 1.35L10.2188 1.35L6.61876 4.95L4.81876 3.15C4.44375 2.775 3.69376 2.775 3.31875 3.15L0.16875 6.3C-0.05625 6.525 -0.05625 6.975 0.16875 7.2C0.318762 7.35001 0.468756 7.425 0.61875 7.425C0.768762 7.425 0.918756 7.425 1.06875 7.2L3.99375 4.275L5.79375 6.075C6.16876 6.45001 6.91875 6.45001 7.29376 6.075L11.0438 2.32501L11.0438 4.20001C11.0438 4.575 11.3438 4.87501 11.7188 4.87501C12.0937 4.87501 12.3938 4.575 12.3938 4.20001L12.3938 0.825011L12.3938 0.600011L12.2438 0.225Z"
|
||||
fill="#00B87A">
|
||||
</path>
|
||||
</svg>
|
||||
|
||||
</span>
|
||||
<span class="t1">4%</span>
|
||||
<span class="t2">(30 days)</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item day">
|
||||
<span class="text">3250天</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</template>
|
||||
<style scoped lang="scss">
|
||||
.content {
|
||||
font-family: "Alibaba-PuHuiTi-Bold";
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.day {
|
||||
background-color: transparent !important;
|
||||
background-image: url('/assets/dayImg.png');
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
padding: 0 !important;
|
||||
background-position: 0 0;
|
||||
position: relative;
|
||||
|
||||
.text {
|
||||
position: absolute;
|
||||
bottom: 40px;
|
||||
left: 50%;
|
||||
// margin-left: 5px;
|
||||
transform: translateX(-50%);
|
||||
color: rgba(24, 109, 245, 1);
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.item {
|
||||
background-color: rgba(240, 249, 255, 1);
|
||||
padding: 30px 15px;
|
||||
border-radius: 20px;
|
||||
text-align: center;
|
||||
// flex: 1;
|
||||
width: 450px;
|
||||
// height: 250px;
|
||||
margin: 20px 0;
|
||||
|
||||
.top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-align: left;
|
||||
|
||||
.icon {
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
margin-top: -20px;
|
||||
}
|
||||
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.t1 {
|
||||
font-size: 26px;
|
||||
}
|
||||
|
||||
.t2 {
|
||||
font-size: 18px;
|
||||
color: rgba(62, 73, 84, 1);
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 10px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.bottom {
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.t1 {
|
||||
color: rgba(0, 184, 122, 1);
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.t2 {
|
||||
color: rgba(150, 155, 160, 1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
:deep(.el-card__body) {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.box {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
145
src/views/pvSystem/mainSystemDiagram/components/sjjk.vue
Normal file
@ -0,0 +1,145 @@
|
||||
<template>
|
||||
<RightTitle subtitle="实时数据监控" icon="sjjk" />
|
||||
|
||||
<div class="sjjk-container">
|
||||
<div class="chart-container">
|
||||
<div id="chart" class="echarts-wrapper" style="width: 800px; height: 200px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import RightTitle from './RightTitle.vue';
|
||||
import * as echarts from 'echarts';
|
||||
import { onMounted } from 'vue';
|
||||
|
||||
onMounted(() => {
|
||||
// 初始化图表
|
||||
const myChart = echarts.init(document.getElementById('chart'));
|
||||
|
||||
// 图表配置项
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow',
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
data: ['电压(kv)', '电流(A)', '功率(MW)'],
|
||||
},
|
||||
grid: {
|
||||
top: '15%',
|
||||
left: '12%',
|
||||
right: '12%',
|
||||
bottom: '20%',
|
||||
containLabel: true,
|
||||
show: false
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: ['发电机1', '发电机2', '发电机3', '发电机4', '发电机5', '发电机6'],
|
||||
axisLabel: {
|
||||
interval: 0,
|
||||
rotate: 45
|
||||
}
|
||||
},
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
name: '电压(kv)/电流(A)',
|
||||
splitLine: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'value',
|
||||
name: '功率(MW)',
|
||||
splitLine: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: '电压(kv)',
|
||||
type: 'bar',
|
||||
data: [80, 70, 55, 80, 80, 80],
|
||||
itemStyle: {
|
||||
color: 'rgba(0, 102, 255, 1)' // 电压颜色
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '电流(A)',
|
||||
type: 'bar',
|
||||
data: [65, 80, 65, 65, 65, 65],
|
||||
itemStyle: {
|
||||
color: 'rgba(255, 195, 0, 0.8)' // 电流颜色
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '功率(MW)',
|
||||
type: 'bar',
|
||||
yAxisIndex: 1,
|
||||
data: [58, 80, 80, 58, 58, 58],
|
||||
itemStyle: {
|
||||
color: 'rgba(0, 186, 173, 0.5)' // 功率颜色
|
||||
}
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// 渲染图表
|
||||
myChart.setOption(option);
|
||||
|
||||
// 窗口大小变化时,图表自适应
|
||||
window.addEventListener('resize', () => {
|
||||
myChart.resize();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
:deep(.el-card__body) {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.sjjk-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #ffffff;
|
||||
border-radius: 8px;
|
||||
/* padding: 20px; */
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.chart-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
width: 100%;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
width: 100%;
|
||||
max-width: 800px;
|
||||
height: 400px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.echarts-wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 400px;
|
||||
border-radius: 4px;
|
||||
/* box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); */
|
||||
}
|
||||
</style>
|
||||
42
src/views/pvSystem/mainSystemDiagram/index.vue
Normal file
@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<div style="padding: 20px;">
|
||||
<el-row>
|
||||
<el-col :span="15">
|
||||
<el-row>
|
||||
<TitleComponent title="设备情况" subtitle="电站一次监控数据" />
|
||||
<sbqk />
|
||||
</el-row>
|
||||
<el-row>
|
||||
<TitleComponent title="电站一次系统图" subtitle="电站一次监控数据" />
|
||||
<dzt />
|
||||
</el-row>
|
||||
<el-row>
|
||||
<TitleComponent title="运行步骤日志" subtitle="实时更新中" />
|
||||
<!-- <sbqk /> -->
|
||||
</el-row>
|
||||
</el-col>
|
||||
<el-col :span="8" :push="1">
|
||||
<TitleComponent title="运行步骤日志" subtitle="实时更新中" />
|
||||
<el-card style="padding: 0;">
|
||||
<sjjk />
|
||||
<glsc />
|
||||
<czzljl />
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
<style scoped>
|
||||
.el-card {
|
||||
border-radius: 10px;
|
||||
}
|
||||
</style>
|
||||
<script setup>
|
||||
import TitleComponent from '@/components/TitleComponent';
|
||||
import sbqk from './components/sbqk.vue';
|
||||
import sjjk from './components/sjjk.vue';
|
||||
import glsc from './components/glsc.vue';
|
||||
import czzljl from './components/czzljl.vue';
|
||||
import dzt from './components/dzt.vue';
|
||||
</script>
|
||||
363
src/views/pvSystem/operatingCurve/components/box1/qushiLine.vue
Normal file
@ -0,0 +1,363 @@
|
||||
<template>
|
||||
<div class="chart-container">
|
||||
<!-- 图表标题和时间范围选择器 -->
|
||||
<div class="chart-header">
|
||||
<h2>发电量趋势 (kWh)</h2>
|
||||
<div class="chart-actions">
|
||||
<button @click="timeRange = 'day'" :class="{ active: timeRange === 'day' }">日发电</button>
|
||||
<button @click="timeRange = 'week'" :class="{ active: timeRange === 'week' }">周发电</button>
|
||||
<button @click="timeRange = 'month'" :class="{ active: timeRange === 'month' }">月发电</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 图表内容区域 -->
|
||||
<div ref="chartRef" class="chart-content"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, watch, computed } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
// 接收从父组件传入的数据
|
||||
const props = defineProps({
|
||||
quShiData: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
day: {
|
||||
xAxis: [],
|
||||
actualData: [],
|
||||
theoreticalData: []
|
||||
},
|
||||
week: {
|
||||
xAxis: [],
|
||||
actualData: [],
|
||||
theoreticalData: []
|
||||
},
|
||||
month: {
|
||||
xAxis: [],
|
||||
actualData: [],
|
||||
theoreticalData: []
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
// 图表DOM引用
|
||||
const chartRef = ref(null);
|
||||
// 时间范围状态
|
||||
const timeRange = ref('day');
|
||||
// 图表实例
|
||||
let chartInstance = null;
|
||||
|
||||
// 定义颜色常量
|
||||
const ACTUAL_COLOR = '#2A82E4';
|
||||
const THEORETICAL_COLOR = '#67C23A';
|
||||
|
||||
// 根据时间范围计算当前显示的数据
|
||||
const chartData = computed(() => {
|
||||
// 使用传入的数据,如果不存在则使用默认数据
|
||||
const dataForRange = props.quShiData[timeRange.value] || props.quShiData.day;
|
||||
return {
|
||||
xAxis: dataForRange.xAxis,
|
||||
actualData: dataForRange.actualData,
|
||||
theoreticalData: dataForRange.theoreticalData
|
||||
};
|
||||
});
|
||||
|
||||
// 初始化图表
|
||||
const initChart = () => {
|
||||
if (chartRef.value && !chartInstance) {
|
||||
chartInstance = echarts.init(chartRef.value);
|
||||
}
|
||||
|
||||
const data = chartData.value;
|
||||
|
||||
const option = {
|
||||
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
// 悬停线相关设置
|
||||
axisPointer: {
|
||||
type: 'cross',
|
||||
lineStyle: {
|
||||
color: '#2A82E4',
|
||||
width: 2
|
||||
}
|
||||
},
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.9)',
|
||||
borderColor: '#e8e8e8',
|
||||
borderWidth:2 ,
|
||||
textStyle: {
|
||||
color: '#333'
|
||||
},
|
||||
formatter: function (params) {
|
||||
const date = params[0].axisValue;
|
||||
const actualValue = params[0].value;
|
||||
const theoreticalValue = params[1].value;
|
||||
|
||||
return `
|
||||
<div style="font-weight: bold;">${date}</div>
|
||||
<div style="color: ${ACTUAL_COLOR};">实际发电量: ${actualValue}</div>
|
||||
<div style="color: ${THEORETICAL_COLOR};">理论发电量: ${theoreticalValue}</div>
|
||||
`;
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
right: 'center',
|
||||
data: ['实际发电', '理论发电'],
|
||||
top: 0,
|
||||
itemWidth: 10,
|
||||
itemHeight: 10,
|
||||
icon: 'stack',
|
||||
textStyle: {
|
||||
fontSize: 12,
|
||||
color: '#666'
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '10%',
|
||||
top: '10%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: data.xAxis,
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#dcdfe6'
|
||||
}
|
||||
},
|
||||
axisLabel: {
|
||||
color: '#3E4954',
|
||||
fontSize: 12
|
||||
},
|
||||
axisTick: {
|
||||
show: false // 去除刻度线
|
||||
},
|
||||
splitLine: {
|
||||
show: true, //显示分割线
|
||||
lineStyle: {
|
||||
type: 'dashed' // 设置分割线为虚线
|
||||
}
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#dcdfe6'
|
||||
}
|
||||
},
|
||||
axisLabel: {
|
||||
color: '#3E4954',
|
||||
fontSize: 12
|
||||
},
|
||||
axisTick: {
|
||||
show: false // 去除刻度线
|
||||
},
|
||||
splitLine: {
|
||||
show: true, //显示分割线
|
||||
lineStyle: {
|
||||
type: 'dashed' // 设置分割线为虚线
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '实际发电',
|
||||
type: 'line',
|
||||
data: data.actualData,
|
||||
smooth: true,
|
||||
symbol: 'circle',
|
||||
symbolSize: 6,
|
||||
// 开始不显示拐点, 鼠标经过显示
|
||||
showSymbol: false,
|
||||
// 设置拐点颜色以及边框
|
||||
itemStyle: {
|
||||
color: ACTUAL_COLOR
|
||||
},
|
||||
lineStyle: {
|
||||
color: ACTUAL_COLOR,
|
||||
width: 3
|
||||
},
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
color: '#2A82E4',
|
||||
borderColor: '#fff',
|
||||
borderWidth: 3
|
||||
}
|
||||
},
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: `rgba(${parseInt(ACTUAL_COLOR.slice(1, 3), 16)}, ${parseInt(ACTUAL_COLOR.slice(3, 5), 16)}, ${parseInt(ACTUAL_COLOR.slice(5, 7), 16)}, 0.2)`
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: `rgba(${parseInt(ACTUAL_COLOR.slice(1, 3), 16)}, ${parseInt(ACTUAL_COLOR.slice(3, 5), 16)}, ${parseInt(ACTUAL_COLOR.slice(5, 7), 16)}, 0.05)`
|
||||
}
|
||||
])
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '理论发电',
|
||||
type: 'line',
|
||||
data: data.theoreticalData,
|
||||
smooth: true,
|
||||
symbol: 'circle',
|
||||
symbolSize: 6,
|
||||
itemStyle: {
|
||||
color: THEORETICAL_COLOR
|
||||
},
|
||||
lineStyle: {
|
||||
color: THEORETICAL_COLOR,
|
||||
width: 3
|
||||
},
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
color: '#67C23A',
|
||||
borderColor: '#fff',
|
||||
borderWidth: 3
|
||||
}
|
||||
},
|
||||
// 开始不显示拐点, 鼠标经过显示
|
||||
showSymbol: false,
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{
|
||||
offset: 0,
|
||||
color: `rgba(${parseInt(THEORETICAL_COLOR.slice(1, 3), 16)}, ${parseInt(THEORETICAL_COLOR.slice(3, 5), 16)}, ${parseInt(THEORETICAL_COLOR.slice(5, 7), 16)}, 0.2)`
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: `rgba(${parseInt(THEORETICAL_COLOR.slice(1, 3), 16)}, ${parseInt(THEORETICAL_COLOR.slice(3, 5), 16)}, ${parseInt(THEORETICAL_COLOR.slice(5, 7), 16)}, 0.05)`
|
||||
}
|
||||
])
|
||||
}
|
||||
},
|
||||
]
|
||||
};
|
||||
|
||||
chartInstance.setOption(option);
|
||||
};
|
||||
|
||||
// 响应窗口大小变化
|
||||
const handleResize = () => {
|
||||
if (chartInstance) {
|
||||
chartInstance.resize();
|
||||
}
|
||||
};
|
||||
|
||||
// 生命周期钩子
|
||||
onMounted(() => {
|
||||
initChart();
|
||||
window.addEventListener('resize', handleResize);
|
||||
|
||||
// 清理函数
|
||||
return () => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
if (chartInstance) {
|
||||
chartInstance.dispose();
|
||||
chartInstance = null;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// 监听时间范围变化,更新图表
|
||||
watch(timeRange, () => {
|
||||
initChart();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.chart-container {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
height: 500px;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.chart-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 15px 20px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.chart-header h2 {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.chart-actions button {
|
||||
background: none;
|
||||
border: 1px solid #e0e0e0;
|
||||
padding: 5px 12px;
|
||||
border-radius: 4px;
|
||||
margin-left: 8px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.chart-actions button.active {
|
||||
background-color: #1890ff;
|
||||
color: white;
|
||||
border-color: #1890ff;
|
||||
}
|
||||
|
||||
.chart-content {
|
||||
width: 100%;
|
||||
height: calc(100% - 54px);
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.chart-container {
|
||||
height: 450px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.chart-container {
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
.chart-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.chart-actions {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.chart-actions button {
|
||||
margin: 0;
|
||||
flex: 1;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.chart-actions button:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.model {
|
||||
padding: 20px;
|
||||
background-color: rgba(242, 248, 252, 1);
|
||||
}
|
||||
</style>
|
||||
240
src/views/pvSystem/operatingCurve/components/box1/statusBox.vue
Normal file
177
src/views/pvSystem/operatingCurve/components/box2/duibiPie.vue
Normal file
@ -0,0 +1,177 @@
|
||||
<template>
|
||||
<div class="chart-container">
|
||||
<div class="chart-header">
|
||||
<h2>性能比分析</h2>
|
||||
<p>当前系统性能比为83%,高于同行水平(78%),表明系统运行状态良好。</p>
|
||||
</div>
|
||||
<div ref="chartRef" class="chart-content"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
// 引入 ECharts 核心模块
|
||||
import * as echarts from 'echarts';
|
||||
import 'echarts-liquidfill'
|
||||
// 接收从父组件传入的数据
|
||||
const props = defineProps({
|
||||
duibiPieData: {
|
||||
type: Number,
|
||||
default: () => ({
|
||||
num:0
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
const chartRef = ref(null);
|
||||
let myChart = null;
|
||||
|
||||
onMounted(() => {
|
||||
// 组件挂载后初始化图表
|
||||
initChart();
|
||||
});
|
||||
|
||||
const initChart = () => {
|
||||
// 获取 DOM 容器
|
||||
const chartDom = chartRef.value;
|
||||
// 初始化 ECharts 实例
|
||||
myChart = echarts.init(chartDom);
|
||||
|
||||
// 使用传入的数据,如果不存在则使用默认数据
|
||||
const { num } = props.duibiPieData;
|
||||
|
||||
// 配置图表参数
|
||||
const option = {
|
||||
title: {
|
||||
show: true,
|
||||
text: '性能比',
|
||||
x: '50%',
|
||||
y: '60%',
|
||||
z: 10,
|
||||
textAlign: 'center',
|
||||
textStyle: {
|
||||
color: '#ffffff',
|
||||
fontSize: 16,
|
||||
fontWeight: 500
|
||||
},
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: "liquidFill",
|
||||
z: 2,
|
||||
radius: "85%",
|
||||
center: ["50%", "50%"],
|
||||
data: [num/100],
|
||||
label: {
|
||||
fontSize: 30, // 设置数据字体大小
|
||||
color: "#165DFF", // 设置数据字体颜色
|
||||
},
|
||||
backgroundStyle: {
|
||||
color: "#fff",
|
||||
},
|
||||
outline: {
|
||||
show: true,
|
||||
itemStyle: {
|
||||
borderWidth: 3, // 设置外边框的大小
|
||||
borderColor: "#165DFF",// 设置外边框的颜色
|
||||
},
|
||||
borderDistance: 0,
|
||||
},
|
||||
color: [
|
||||
{
|
||||
type: "linear",
|
||||
x: 0,
|
||||
y: 0,
|
||||
x2: 0,
|
||||
y2: 1,
|
||||
colorStops: [
|
||||
{
|
||||
offset: 1,
|
||||
color: "rgba(22, 93, 255,0.8)", //下
|
||||
},
|
||||
{
|
||||
offset: 0,
|
||||
color: "rgba(22, 93, 255,0.2)",
|
||||
},
|
||||
],
|
||||
globalCoord: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// 将配置项设置到图表实例
|
||||
myChart.setOption(option);
|
||||
};
|
||||
|
||||
// 组件卸载时,销毁图表实例(避免内存泄漏)
|
||||
onUnmounted(() => {
|
||||
if (myChart) {
|
||||
myChart.dispose();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.chart-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0 16px;
|
||||
box-sizing: border-box;
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.chart-header {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.chart-header h2 {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.chart-header p {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.chart-content {
|
||||
width: 100%;
|
||||
height: calc(100% - 80px);
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.chart-container {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.chart-content {
|
||||
height: calc(100% - 70px);
|
||||
min-height: 280px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.chart-container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.chart-header h2 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.chart-header p {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.chart-content {
|
||||
height: calc(100% - 65px);
|
||||
min-height: 250px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
220
src/views/pvSystem/operatingCurve/components/box2/powerBar.vue
Normal file
@ -0,0 +1,220 @@
|
||||
<template>
|
||||
<div class="chart-container">
|
||||
<div class="chart-header">
|
||||
<h2>发电量预测对比</h2>
|
||||
<p>实际发电量与预测值偏差在u±5%以内,处于合理范围,预测模型准确度较高。</p>
|
||||
</div>
|
||||
<div class="chart-content" ref="chartRef"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted, nextTick } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
// 接收从父组件传入的数据
|
||||
const props = defineProps({
|
||||
powerBarData: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
xAxis: ['上周', '本周'],
|
||||
actualData: [],
|
||||
forecastData: []
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
const chartRef = ref<HTMLDivElement>();
|
||||
let chartInstance: echarts.ECharts | null = null;
|
||||
|
||||
// 初始化图表
|
||||
const initChart = async () => {
|
||||
await nextTick();
|
||||
if (!chartRef.value) return;
|
||||
|
||||
// 销毁已存在的图表实例
|
||||
if (chartInstance) {
|
||||
chartInstance.dispose();
|
||||
}
|
||||
|
||||
// 创建新的图表实例
|
||||
chartInstance = echarts.init(chartRef.value);
|
||||
|
||||
// 使用传入的数据,如果不存在则使用默认数据
|
||||
const data = props.powerBarData;
|
||||
|
||||
// 图表配置
|
||||
const option = {
|
||||
tooltip: {
|
||||
backgroundColor: 'rgba(13, 64, 71, 0.50)',
|
||||
borderColor: 'rgba(143, 225, 252, 0.60)',
|
||||
textStyle: {
|
||||
color: '#fff'
|
||||
},
|
||||
padding: 10,
|
||||
borderRadius: 4,
|
||||
},
|
||||
legend: {
|
||||
data: ['实际发电量', '预测发电量'],
|
||||
itemWidth: 14,
|
||||
itemHeight: 10,
|
||||
itemGap: 4,
|
||||
textStyle: {
|
||||
fontSize: 12,
|
||||
color: '#666'
|
||||
}
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: data.xAxis,
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
},
|
||||
axisLabel: {
|
||||
textStyle: {
|
||||
color: '#3E4954',
|
||||
fontSize: 12
|
||||
}
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#ccc'
|
||||
}
|
||||
},
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
min: 0,
|
||||
max: 10000,
|
||||
interval: 5000,
|
||||
axisLine: {
|
||||
show: false
|
||||
},
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
color: '#f0f0f0',
|
||||
type: 'dashed'
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '实际发电量',
|
||||
type: 'bar',
|
||||
data: data.actualData,
|
||||
itemStyle: {
|
||||
color: '#1a77dd',
|
||||
borderRadius: [8, 8, 0, 0] // 设置圆角,上左、上右、下右、下左
|
||||
},
|
||||
barWidth: 60
|
||||
},
|
||||
{
|
||||
name: '预测发电量',
|
||||
type: 'bar',
|
||||
data: data.forecastData,
|
||||
itemStyle: {
|
||||
color: '#7c3aed',
|
||||
borderRadius: [8, 8, 0, 0] // 设置圆角,上左、上右、下右、下左
|
||||
},
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
color: '#6d28d9' // 悬停时的颜色
|
||||
}
|
||||
},
|
||||
barWidth: 60
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
// 设置图表配置
|
||||
chartInstance.setOption(option);
|
||||
};
|
||||
|
||||
// 窗口大小改变时重新调整图表
|
||||
const handleResize = () => {
|
||||
if (chartInstance) {
|
||||
chartInstance.resize();
|
||||
}
|
||||
};
|
||||
|
||||
// 生命周期钩子
|
||||
onMounted(() => {
|
||||
initChart();
|
||||
window.addEventListener('resize', handleResize);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
if (chartInstance) {
|
||||
chartInstance.dispose();
|
||||
chartInstance = null;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.chart-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0 16px;
|
||||
box-sizing: border-box;
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.chart-header {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.chart-header h2 {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.chart-header p {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.chart-content {
|
||||
width: 100%;
|
||||
height: calc(100% - 80px);
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.chart-container {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.chart-content {
|
||||
height: calc(100% - 70px);
|
||||
min-height: 280px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.chart-container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.chart-header h2 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.chart-header p {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.chart-content {
|
||||
height: calc(100% - 65px);
|
||||
min-height: 250px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
340
src/views/pvSystem/operatingCurve/components/nibianqiLine.vue
Normal file
@ -0,0 +1,340 @@
|
||||
<template>
|
||||
<div class="chart-container">
|
||||
<!-- 逆变器输出与汇流箱电流 图表内容区域 -->
|
||||
<div ref="chartRef" class="chart-content"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, watch } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
// 接收从父组件传入的数据
|
||||
const props = defineProps({
|
||||
nibianqiData: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
xAxis: [],
|
||||
inverterData: [],
|
||||
combinerBoxData: []
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
// 图表DOM引用
|
||||
const chartRef = ref(null);
|
||||
// 图表实例
|
||||
let chartInstance = null;
|
||||
|
||||
// 定义颜色常量
|
||||
const ACTUAL_COLOR = '#7948EA';
|
||||
const THEORETICAL_COLOR = '#0DFFEF';
|
||||
|
||||
// 十六进制颜色转rgba工具函数
|
||||
const hexToRgba = (hex, opacity) => {
|
||||
// 移除#号
|
||||
hex = hex.replace('#', '');
|
||||
|
||||
// 解析RGB值
|
||||
const r = parseInt(hex.substring(0, 2), 16);
|
||||
const g = parseInt(hex.substring(2, 4), 16);
|
||||
const b = parseInt(hex.substring(4, 6), 16);
|
||||
|
||||
return `rgba(${r}, ${g}, ${b}, ${opacity})`;
|
||||
};
|
||||
// 初始化图表
|
||||
const initChart = () => {
|
||||
if (chartRef.value && !chartInstance) {
|
||||
chartInstance = echarts.init(chartRef.value);
|
||||
}
|
||||
|
||||
// 使用传入的数据,如果不存在则使用默认数据
|
||||
const { xAxis, inverterData, combinerBoxData } = props.nibianqiData;
|
||||
|
||||
const option = {
|
||||
title: {
|
||||
text: '逆变器输出与汇流箱电流',
|
||||
top: 5,
|
||||
textStyle: {
|
||||
color: '#111111', // 标题颜色
|
||||
fontWeight: 'bold',
|
||||
fontSize: 16,
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
backgroundColor: 'rgba(33,56,77,1)',
|
||||
borderColor: 'rgba(33,56,77,1)',
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
fontSize: 14
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
right: 'center',
|
||||
top: 4,
|
||||
itemWidth: 10,
|
||||
itemHeight: 10,
|
||||
itemGap: 25,
|
||||
icon: 'stack',
|
||||
},
|
||||
grid: {
|
||||
top: '30%',
|
||||
right: '4%',
|
||||
bottom: '4%',
|
||||
left: '6%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
data: xAxis,
|
||||
type: 'category',
|
||||
boundaryGap: true,
|
||||
axisLabel: {
|
||||
textStyle: {
|
||||
color: '#3E4954',
|
||||
fontSize: 12
|
||||
}
|
||||
},
|
||||
axisTick: {
|
||||
show: false // 去除刻度线
|
||||
},
|
||||
},
|
||||
yAxis: [
|
||||
{
|
||||
name: '逆变器输出:KW',
|
||||
position: 'left',
|
||||
type: 'value',
|
||||
min: 0, // 统一最小刻度
|
||||
max: 120, // 统一最大刻度
|
||||
axisLabel: {
|
||||
textStyle: {
|
||||
color: '#3E4954',
|
||||
fontSize: 12
|
||||
}
|
||||
},
|
||||
axisTick: {
|
||||
show: false // 去除刻度线
|
||||
},
|
||||
splitLine: {
|
||||
show: false,// y轴分割线
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '汇流箱电流',
|
||||
position: 'right',
|
||||
type: 'value',
|
||||
min: 0, // 统一最小刻度
|
||||
max: 120, // 统一最大刻度
|
||||
axisLabel: {
|
||||
textStyle: {
|
||||
color: '#3E4954',
|
||||
fontSize: 12
|
||||
}
|
||||
},
|
||||
|
||||
axisTick: {
|
||||
show: false // 去除刻度线
|
||||
},
|
||||
|
||||
splitLine: {
|
||||
show: false,// y轴分割线
|
||||
}
|
||||
},
|
||||
],
|
||||
|
||||
series: [
|
||||
{
|
||||
name: '逆变器输出',
|
||||
smooth: true,
|
||||
type: 'line',
|
||||
yAxisIndex: 1, // 关联到右侧y轴
|
||||
lineStyle: {
|
||||
width: 4 // 线条宽度,单位是 px
|
||||
},
|
||||
// 填充颜色设置
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
[
|
||||
{
|
||||
offset: 0,
|
||||
color: hexToRgba(ACTUAL_COLOR, 0.4) // 使用ACTUAL_COLOR并设置透明度
|
||||
},
|
||||
{
|
||||
offset: 0.9,
|
||||
color: hexToRgba(ACTUAL_COLOR, 0) // 使用ACTUAL_COLOR并设置透明度为0
|
||||
}
|
||||
],
|
||||
false
|
||||
),
|
||||
shadowColor: 'rgba(0, 0, 0, 0.1)'
|
||||
},
|
||||
// 拐点
|
||||
showSymbol: true,
|
||||
symbolSize: 10,
|
||||
// 设置拐点颜色以及边框
|
||||
itemStyle: {
|
||||
color: ACTUAL_COLOR
|
||||
},
|
||||
data: inverterData
|
||||
},
|
||||
{
|
||||
name: '汇流箱电流',
|
||||
smooth: true,
|
||||
type: 'line',
|
||||
lineStyle: {
|
||||
width: 4 // 线条宽度,单位是 px
|
||||
},
|
||||
// 填充颜色设置
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
[
|
||||
{
|
||||
offset: 0,
|
||||
color: hexToRgba(THEORETICAL_COLOR, 0.4) // 使用THEORETICAL_COLOR并设置透明度
|
||||
},
|
||||
{
|
||||
offset: 0.9,
|
||||
color: hexToRgba(THEORETICAL_COLOR, 0) // 使用THEORETICAL_COLOR并设置透明度为0
|
||||
}
|
||||
],
|
||||
false
|
||||
),
|
||||
shadowColor: 'rgba(0, 0, 0, 0.1)'
|
||||
},
|
||||
// 拐点
|
||||
showSymbol: true,
|
||||
symbolSize: 10,
|
||||
// 设置拐点颜色以及边框
|
||||
itemStyle: {
|
||||
color: THEORETICAL_COLOR
|
||||
},
|
||||
data: combinerBoxData
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
chartInstance.setOption(option);
|
||||
};
|
||||
|
||||
// 响应窗口大小变化
|
||||
const handleResize = () => {
|
||||
if (chartInstance) {
|
||||
chartInstance.resize();
|
||||
}
|
||||
};
|
||||
|
||||
// 生命周期钩子
|
||||
onMounted(() => {
|
||||
initChart();
|
||||
window.addEventListener('resize', handleResize);
|
||||
|
||||
// 清理函数
|
||||
return () => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
if (chartInstance) {
|
||||
chartInstance.dispose();
|
||||
chartInstance = null;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.chart-container {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
height: 400px;
|
||||
width: 100%;
|
||||
/* padding: 10px; */
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.chart-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 15px 20px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.chart-header h2 {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.chart-actions button {
|
||||
background: none;
|
||||
border: 1px solid #e0e0e0;
|
||||
padding: 5px 12px;
|
||||
border-radius: 4px;
|
||||
margin-left: 8px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.chart-actions button.active {
|
||||
background-color: #1890ff;
|
||||
color: white;
|
||||
border-color: #1890ff;
|
||||
}
|
||||
|
||||
.chart-content {
|
||||
width: 100%;
|
||||
height: calc(100% - 54px);
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.chart-container {
|
||||
height: 350px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.chart-container {
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.chart-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.chart-actions {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.chart-actions button {
|
||||
margin: 0;
|
||||
flex: 1;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.chart-actions button:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.model {
|
||||
padding: 20px;
|
||||
background-color: rgba(242, 248, 252, 1);
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,261 @@
|
||||
<template>
|
||||
<div class="chart-container">
|
||||
<!--系统效率与日照强度 图表内容区域 -->
|
||||
<div ref="chartRef" class="chart-content"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
// 接收从父组件传入的数据
|
||||
const props = defineProps({
|
||||
riZhaoData: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
xAxis: [],
|
||||
systemEfficiency: [],
|
||||
solarIrradiance: []
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
// 图表DOM引用
|
||||
const chartRef = ref(null);
|
||||
// 图表实例
|
||||
let chartInstance = null;
|
||||
|
||||
// 定义颜色常量
|
||||
const SYSTEM_EFFICIENCY_COLOR = '#FFD700'; // 黄色 - 系统效率
|
||||
const SOLAR_IRRADIANCE_COLOR = '#1890FF'; // 蓝色 - 日照强度
|
||||
|
||||
// 初始化图表
|
||||
const initChart = () => {
|
||||
if (chartRef.value && !chartInstance) {
|
||||
chartInstance = echarts.init(chartRef.value);
|
||||
}
|
||||
|
||||
// 使用传入的数据,如果不存在则使用默认数据
|
||||
const { xAxis, systemEfficiency, solarIrradiance } = props.riZhaoData;
|
||||
|
||||
const option = {
|
||||
title: {
|
||||
text: '系统效率与日照强度',
|
||||
top: 5,
|
||||
align: 'left',
|
||||
textStyle: {
|
||||
color: '#111111',
|
||||
fontWeight: 'bold',
|
||||
fontSize: 16,
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
backgroundColor: 'rgba(255,255,255,1)',
|
||||
borderColor: '#ddd',
|
||||
borderWidth: 1,
|
||||
textStyle: {
|
||||
color: '#333',
|
||||
fontSize: 14
|
||||
},
|
||||
formatter: function(params) {
|
||||
const systemEfficiency = params[0].value;
|
||||
const solarIrradiance = params[1].value;
|
||||
return `
|
||||
<div style="padding: 5px;">
|
||||
<div style="color: ${params[0].color};">系统效率: ${systemEfficiency}%</div>
|
||||
<div style="color: ${params[1].color};">日照强度: ${solarIrradiance}</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
top: 30,
|
||||
left: 'center',
|
||||
itemWidth: 10,
|
||||
itemHeight: 10,
|
||||
itemGap: 25,
|
||||
data: ['系统效率', '日照强度'],
|
||||
textStyle: {
|
||||
color: '#666',
|
||||
fontSize: 12
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
top: '30%',
|
||||
right: '10%',
|
||||
bottom: '10%',
|
||||
left: '6%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
data: xAxis,
|
||||
type: 'category',
|
||||
boundaryGap: true,
|
||||
axisLabel: {
|
||||
textStyle: {
|
||||
color: '#666',
|
||||
fontSize: 12
|
||||
}
|
||||
},
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#ddd'
|
||||
}
|
||||
}
|
||||
},
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
name: '系统效率(%)',
|
||||
nameTextStyle: {
|
||||
color: '#666',
|
||||
fontSize: 12
|
||||
},
|
||||
min: 0,
|
||||
max: 20,
|
||||
interval: 5,
|
||||
axisLabel: {
|
||||
textStyle: {
|
||||
color: '#666',
|
||||
fontSize: 12
|
||||
}
|
||||
},
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
axisLine: {
|
||||
show: false
|
||||
},
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
color: '#f0f0f0',
|
||||
type: 'dashed'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'value',
|
||||
name: '日照强度',
|
||||
nameTextStyle: {
|
||||
color: '#666',
|
||||
fontSize: 12
|
||||
},
|
||||
min: 0,
|
||||
max: 800,
|
||||
interval: 200,
|
||||
axisLabel: {
|
||||
textStyle: {
|
||||
color: '#666',
|
||||
fontSize: 12
|
||||
}
|
||||
},
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
axisLine: {
|
||||
show: false
|
||||
},
|
||||
splitLine: {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: '系统效率',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
showSymbol: false,
|
||||
symbol: 'circle',
|
||||
symbolSize: 6,
|
||||
emphasis: {
|
||||
showSymbol: true,
|
||||
symbolSize: 10
|
||||
},
|
||||
yAxisIndex: 0,
|
||||
lineStyle: {
|
||||
width: 2,
|
||||
color: SYSTEM_EFFICIENCY_COLOR
|
||||
},
|
||||
itemStyle: {
|
||||
color: SYSTEM_EFFICIENCY_COLOR,
|
||||
borderColor: '#fff',
|
||||
borderWidth: 2
|
||||
},
|
||||
data: systemEfficiency
|
||||
},
|
||||
{
|
||||
name: '日照强度',
|
||||
type: 'bar',
|
||||
yAxisIndex: 1,
|
||||
barWidth: '40%',
|
||||
itemStyle: {
|
||||
color: SOLAR_IRRADIANCE_COLOR
|
||||
},
|
||||
data: solarIrradiance
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
chartInstance.setOption(option);
|
||||
};
|
||||
|
||||
// 响应窗口大小变化
|
||||
const handleResize = () => {
|
||||
if (chartInstance) {
|
||||
chartInstance.resize();
|
||||
}
|
||||
};
|
||||
|
||||
// 生命周期钩子
|
||||
onMounted(() => {
|
||||
initChart();
|
||||
window.addEventListener('resize', handleResize);
|
||||
|
||||
// 清理函数
|
||||
return () => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
if (chartInstance) {
|
||||
chartInstance.dispose();
|
||||
chartInstance = null;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.chart-container {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
height: 400px;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.chart-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.chart-container {
|
||||
height: 350px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.chart-container {
|
||||
height: 300px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
317
src/views/pvSystem/operatingCurve/components/temperatureLine.vue
Normal file
@ -0,0 +1,317 @@
|
||||
<template>
|
||||
<div class="chart-container">
|
||||
<!--组件温度(℃) 图表内容区域 -->
|
||||
<div ref="chartRef" class="chart-content"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, watch } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
// 接收从父组件传入的数据
|
||||
const props = defineProps({
|
||||
temperatureData: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
xAxis: [],
|
||||
componentTemp: [],
|
||||
ambientTemp: []
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
// 图表DOM引用
|
||||
const chartRef = ref(null);
|
||||
// 图表实例
|
||||
let chartInstance = null;
|
||||
|
||||
// 定义颜色常量
|
||||
const ACTUAL_COLOR = '#FF5733';
|
||||
const THEORETICAL_COLOR = '#FF8D1A';
|
||||
|
||||
// 十六进制颜色转rgba工具函数
|
||||
const hexToRgba = (hex, opacity) => {
|
||||
// 移除#号
|
||||
hex = hex.replace('#', '');
|
||||
|
||||
// 解析RGB值
|
||||
const r = parseInt(hex.substring(0, 2), 16);
|
||||
const g = parseInt(hex.substring(2, 4), 16);
|
||||
const b = parseInt(hex.substring(4, 6), 16);
|
||||
|
||||
return `rgba(${r}, ${g}, ${b}, ${opacity})`;
|
||||
};
|
||||
// 初始化图表
|
||||
const initChart = () => {
|
||||
if (chartRef.value && !chartInstance) {
|
||||
chartInstance = echarts.init(chartRef.value);
|
||||
}
|
||||
|
||||
// 使用传入的数据,如果不存在则使用默认数据
|
||||
const { xAxis, componentTemp, ambientTemp } = props.temperatureData;
|
||||
|
||||
const option = {
|
||||
title: {
|
||||
text: '组件温度(℃)',
|
||||
top: 5,
|
||||
textStyle: {
|
||||
color: '#111111', // 标题颜色
|
||||
fontWeight: 'bold',
|
||||
fontSize: 16,
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
backgroundColor: 'rgba(33,56,77,1)',
|
||||
borderColor: 'rgba(33,56,77,1)',
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
fontSize: 14
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
right: '5%',
|
||||
top: 4,
|
||||
itemWidth: 10,
|
||||
itemHeight: 10,
|
||||
itemGap: 25,
|
||||
icon: 'stack',
|
||||
data: ['组件温度', '环境温度']
|
||||
},
|
||||
grid: {
|
||||
top: '20%',
|
||||
right: '4%',
|
||||
bottom: '4%',
|
||||
left: '6%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
data: xAxis,
|
||||
type: 'category',
|
||||
boundaryGap: true,
|
||||
axisLabel: {
|
||||
textStyle: {
|
||||
color: '#3E4954',
|
||||
fontSize: 12
|
||||
}
|
||||
},
|
||||
axisTick: {
|
||||
show: false // 去除刻度线
|
||||
},
|
||||
},
|
||||
yAxis:
|
||||
{
|
||||
type: 'value',
|
||||
min: 0,
|
||||
max: 50,
|
||||
axisLabel: {
|
||||
formatter: '{value} ℃',
|
||||
textStyle: {
|
||||
color: '#3E4954',
|
||||
fontSize: 12
|
||||
}
|
||||
},
|
||||
axisTick: {
|
||||
show: false // 去除刻度线
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#f0f0f0',
|
||||
type: 'dashed'
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '组件温度',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
showSymbol: false, // 取消点显示
|
||||
lineStyle: {
|
||||
width: 4, // 线条宽度,单位是 px
|
||||
color: ACTUAL_COLOR
|
||||
},
|
||||
// 填充颜色设置
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
[
|
||||
{
|
||||
offset: 0,
|
||||
color: hexToRgba(ACTUAL_COLOR, 0.4) // 使用ACTUAL_COLOR并设置透明度
|
||||
},
|
||||
{
|
||||
offset: 0.9,
|
||||
color: hexToRgba(ACTUAL_COLOR, 0) // 使用ACTUAL_COLOR并设置透明度为0
|
||||
}
|
||||
],
|
||||
false
|
||||
),
|
||||
shadowColor: 'rgba(0, 0, 0, 0.1)'
|
||||
},
|
||||
// 设置拐点颜色以及边框
|
||||
itemStyle: {
|
||||
color: ACTUAL_COLOR
|
||||
},
|
||||
data: componentTemp
|
||||
},
|
||||
{
|
||||
name: '环境温度',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
showSymbol: false, // 取消点显示
|
||||
lineStyle: {
|
||||
width: 4, // 线条宽度,单位是 px
|
||||
color: THEORETICAL_COLOR
|
||||
},
|
||||
// 填充颜色设置
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
[
|
||||
{
|
||||
offset: 0,
|
||||
color: hexToRgba(THEORETICAL_COLOR, 0.4) // 使用THEORETICAL_COLOR并设置透明度
|
||||
},
|
||||
{
|
||||
offset: 0.9,
|
||||
color: hexToRgba(THEORETICAL_COLOR, 0) // 使用THEORETICAL_COLOR并设置透明度为0
|
||||
}
|
||||
],
|
||||
false
|
||||
),
|
||||
shadowColor: 'rgba(0, 0, 0, 0.1)'
|
||||
},
|
||||
itemStyle: {
|
||||
color: THEORETICAL_COLOR
|
||||
},
|
||||
data: ambientTemp
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
chartInstance.setOption(option);
|
||||
};
|
||||
|
||||
// 响应窗口大小变化
|
||||
const handleResize = () => {
|
||||
if (chartInstance) {
|
||||
chartInstance.resize();
|
||||
}
|
||||
};
|
||||
|
||||
// 生命周期钩子
|
||||
onMounted(() => {
|
||||
initChart();
|
||||
window.addEventListener('resize', handleResize);
|
||||
|
||||
// 清理函数
|
||||
return () => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
if (chartInstance) {
|
||||
chartInstance.dispose();
|
||||
chartInstance = null;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.chart-container {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
height: 400px;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.chart-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 15px 20px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.chart-header h2 {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.chart-actions button {
|
||||
background: none;
|
||||
border: 1px solid #e0e0e0;
|
||||
padding: 5px 12px;
|
||||
border-radius: 4px;
|
||||
margin-left: 8px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.chart-actions button.active {
|
||||
background-color: #1890ff;
|
||||
color: white;
|
||||
border-color: #1890ff;
|
||||
}
|
||||
|
||||
.chart-content {
|
||||
width: 100%;
|
||||
height: calc(100% - 54px);
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.chart-container {
|
||||
height: 350px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.chart-container {
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.chart-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.chart-actions {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.chart-actions button {
|
||||
margin: 0;
|
||||
flex: 1;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.chart-actions button:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.model {
|
||||
padding: 20px;
|
||||
background-color: rgba(242, 248, 252, 1);
|
||||
}
|
||||
</style>
|
||||
238
src/views/pvSystem/operatingCurve/components/yujing.vue
Normal file
@ -0,0 +1,238 @@
|
||||
<template>
|
||||
<div class="warning-container">
|
||||
<!-- 标题区域 -->
|
||||
<h2 class="main-title">异常参数预警</h2>
|
||||
|
||||
<!-- 预警列表容器,设置最大高度和滚动 -->
|
||||
<div class="warning-list">
|
||||
<!-- 循环渲染预警项 -->
|
||||
<div v-for="(warning, index) in warnings" :key="index"
|
||||
:class="['warning-item', getWarningClass(warning.type)]">
|
||||
<!-- 预警图标 -->
|
||||
<el-icon :class="getIconClass(warning.type)">
|
||||
<template v-if="warning.type === 'error'">
|
||||
<CircleCloseFilled />
|
||||
</template>
|
||||
<template v-if="warning.type === 'warning'">
|
||||
<WarningFilled />
|
||||
</template>
|
||||
<template v-if="warning.type === 'info'">
|
||||
<Odometer />
|
||||
</template>
|
||||
</el-icon>
|
||||
|
||||
<!-- 预警内容 -->
|
||||
<div class="warning-content">
|
||||
<div :class="['warning-title', getTitleClass(warning.type)]">{{ warning.title }}</div>
|
||||
<div class="warning-time">{{ warning.time }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted } from 'vue';
|
||||
import { CircleCloseFilled, InfoFilled, WarningFilled, Odometer } from '@element-plus/icons-vue';
|
||||
|
||||
// 接收从父组件传入的数据
|
||||
const props = defineProps({
|
||||
warningData: {
|
||||
type: Array,
|
||||
default: () => [
|
||||
{
|
||||
type: 'error',
|
||||
title: '无',
|
||||
time: '无'
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
// 使用传入的预警数据
|
||||
const warnings = ref(props.warningData);
|
||||
|
||||
// 根据预警类型获取样式类
|
||||
const getWarningClass = (type) => {
|
||||
return `warning-${type}`;
|
||||
};
|
||||
|
||||
// 根据预警类型获取图标样式类
|
||||
const getIconClass = (type) => {
|
||||
return `${type}-icon`;
|
||||
};
|
||||
|
||||
// 根据预警类型获取标题样式类,确保标题颜色与图标一致
|
||||
const getTitleClass = (type) => {
|
||||
return `${type}-title`;
|
||||
};
|
||||
|
||||
// 监听窗口大小变化,调整组件高度
|
||||
let resizeObserver = null;
|
||||
|
||||
onMounted(() => {
|
||||
// 设置响应式高度
|
||||
const setResponsiveHeight = () => {
|
||||
const warningContainer = document.querySelector('.warning-container');
|
||||
const screenWidth = window.innerWidth;
|
||||
let maxHeight = 400; // 默认高度,与line1组件一致
|
||||
|
||||
if (screenWidth <= 768) {
|
||||
maxHeight = 350;
|
||||
}
|
||||
if (screenWidth <= 480) {
|
||||
maxHeight = 300;
|
||||
}
|
||||
|
||||
if (warningContainer) {
|
||||
warningContainer.style.height = `${maxHeight}px`;
|
||||
}
|
||||
};
|
||||
|
||||
// 初始设置
|
||||
setResponsiveHeight();
|
||||
|
||||
// 创建ResizeObserver监听窗口大小变化
|
||||
if (window.ResizeObserver) {
|
||||
resizeObserver = new ResizeObserver(setResponsiveHeight);
|
||||
resizeObserver.observe(document.documentElement);
|
||||
} else {
|
||||
// 降级方案:使用窗口resize事件
|
||||
window.addEventListener('resize', setResponsiveHeight);
|
||||
}
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
// 清理监听器,避免内存泄漏
|
||||
if (resizeObserver) {
|
||||
resizeObserver.disconnect();
|
||||
} else {
|
||||
window.removeEventListener('resize', () => { });
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.warning-container {
|
||||
height: 400px;
|
||||
/* 与line1组件一致的高度 */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.main-title {
|
||||
color: '#111111';
|
||||
font-weight:bold;
|
||||
font-size: 16px;
|
||||
margin-bottom: 20px;
|
||||
|
||||
|
||||
}
|
||||
|
||||
.warning-list {
|
||||
overflow-y: auto;
|
||||
padding-right: 10px;
|
||||
/* 为滚动条预留空间 */
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* 滚动条样式优化 */
|
||||
.warning-list::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.warning-list::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.warning-list::-webkit-scrollbar-thumb {
|
||||
background: #ccc;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.warning-list::-webkit-scrollbar-thumb:hover {
|
||||
background: #aaa;
|
||||
}
|
||||
|
||||
/* 响应式设计 - 与line1组件保持一致 */
|
||||
@media (max-width: 768px) {
|
||||
.warning-container {
|
||||
height: 350px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.warning-container {
|
||||
height: 300px;
|
||||
}
|
||||
}
|
||||
|
||||
.warning-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 16px;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 12px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.warning-item:hover {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
/* 不同类型预警的背景色 */
|
||||
.warning-error {
|
||||
background-color: #fff1f0;
|
||||
}
|
||||
|
||||
.warning-warning {
|
||||
background-color: #fffbe6;
|
||||
}
|
||||
|
||||
.warning-info {
|
||||
background-color: #e6f7ff;
|
||||
}
|
||||
|
||||
/* 图标和标题颜色保持一致 */
|
||||
.error-icon,
|
||||
.error-title {
|
||||
color: #f5222d !important;
|
||||
/* 红色,使用 !important 确保优先级 */
|
||||
}
|
||||
|
||||
.warning-icon,
|
||||
.warning-title {
|
||||
color: #faad14;
|
||||
}
|
||||
|
||||
.info-icon,
|
||||
.info-title {
|
||||
color: #00BAAD;
|
||||
}
|
||||
|
||||
/* 图标通用样式 */
|
||||
.el-icon {
|
||||
font-size: 20px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.warning-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.warning-content .warning-title {
|
||||
font-size: 16px;
|
||||
margin-bottom: 4px;
|
||||
font-weight: bold;
|
||||
/* 颜色由动态类名(如.error-title)控制,不设置固定颜色 */
|
||||
}
|
||||
|
||||
.warning-content .warning-time {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
</style>
|
||||
245
src/views/pvSystem/operatingCurve/index.vue
Normal file
@ -0,0 +1,245 @@
|
||||
<template>
|
||||
<div class="model">
|
||||
<!-- 标题栏 -->
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="12">
|
||||
<TitleComponent title="运行参数曲线分析" subtitle="实时监控光伏系统关键运行参数,分析性能趋势" />
|
||||
</el-col>
|
||||
<!-- 外层col:控制整体宽度并右对齐,同时作为flex容器 -->
|
||||
<el-col :span="12" style="display: flex; justify-content: flex-end; align-items: center;">
|
||||
<!-- 子col1:下拉 -->
|
||||
<el-col :span="4">
|
||||
<el-select placeholder="请选择电站">
|
||||
<el-option label="所有电站" value="all"></el-option>
|
||||
</el-select>
|
||||
</el-col>
|
||||
<!-- 子col2:下拉框容器 -->
|
||||
<el-col :span="4">
|
||||
<el-select placeholder="请选择月份">
|
||||
<el-option label="所有月份" value="all"></el-option>
|
||||
</el-select>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<el-button type="primary">
|
||||
导出数据
|
||||
<el-icon class="el-icon--right">
|
||||
<UploadFilled />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
</el-col>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!-- 图示区域1 -->
|
||||
<el-row :gutter="24" class="flex-container">
|
||||
<el-col :span="16" class="flex-item">
|
||||
<el-card>
|
||||
<!-- 状态 -->
|
||||
<statusBox :statusData="mockData.statusData"></statusBox>
|
||||
<!-- 发电量趋势 -->
|
||||
<qushiLine class="qushi" :quShiData="mockData.quShiData"></qushiLine>
|
||||
</el-card>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="8" class="flex-item">
|
||||
<el-card>
|
||||
<!-- 发电量预测对比 -->
|
||||
<powerBar :powerBarData="mockData.powerBarData"></powerBar>
|
||||
</el-card>
|
||||
<el-card>
|
||||
<!-- 性能分析 -->
|
||||
<duibiPie :duibiPieData="mockData.duibiPieData"></duibiPie>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!-- 图示区域2-->
|
||||
<el-row :gutter="24" style="margin-top: 20px;" class="flex-container">
|
||||
<el-col :span="16">
|
||||
<el-card>
|
||||
<!-- 系统效率与日照强度 -->
|
||||
<rizhaoqiangduBar :riZhaoData="mockData.rizhaoqiangduData"></rizhaoqiangduBar>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-card>
|
||||
<!-- 组件温度 -->
|
||||
<temperatureLine class="line" :temperatureData="mockData.temperatureData"></temperatureLine>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!-- 图示区域3 -->
|
||||
<el-row :gutter="24" style="margin-top: 20px;">
|
||||
<el-col :span="16">
|
||||
<el-card>
|
||||
<!-- 逆变器输出与汇流箱电流 -->
|
||||
<nibianqiLine class="line" :nibianqiData="mockData.nibianqiData"></nibianqiLine>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<!-- 异常参数预警 -->
|
||||
<el-card>
|
||||
<yujing :warningData="mockData.yujingData"></yujing>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import TitleComponent from '@/components/TitleComponent/index.vue';
|
||||
import StatusBox from '@/views/pvSystem/operatingCurve/components/box1/statusBox.vue'
|
||||
import QushiLine from '@/views/pvSystem/operatingCurve/components/box1/qushiLine.vue'
|
||||
import powerBar from '@/views/pvSystem/operatingCurve/components/box2/powerBar.vue'
|
||||
import nibianqiLine from '@/views/pvSystem/operatingCurve/components/nibianqiLine.vue';
|
||||
import temperatureLine from '@/views/pvSystem/operatingCurve/components/temperatureLine.vue';
|
||||
import rizhaoqiangduBar from '@/views/pvSystem/operatingCurve/components/rizhaoqiangduBar.vue';
|
||||
import duibiPie from '@/views/pvSystem/operatingCurve/components/box2/duibiPie.vue';
|
||||
import yujing from '@/views/pvSystem/operatingCurve/components/yujing.vue'
|
||||
|
||||
// 模拟接口数据
|
||||
const mockData = ref({
|
||||
// 状态卡片数据
|
||||
statusData: {
|
||||
totalPower: '3,456.8KWh',
|
||||
totalPowerChange: '8.2%',
|
||||
efficiency: '18.7%',
|
||||
efficiencyChange: '-0.3%',
|
||||
temperature: '42.3°C',
|
||||
temperatureChange: '2.1°C',
|
||||
sunlight: '865 W/m²',
|
||||
sunlightChange: '12.5%'
|
||||
},
|
||||
|
||||
// 发电量趋势数据
|
||||
quShiData: {
|
||||
day: {
|
||||
xAxis: ['03-09', '03-10', '03-11', '03-12', '03-13', '03-14', '03-15', '03-16'],
|
||||
actualData: [35, 65, 60, 55, 45, 50, 45, 30],
|
||||
theoreticalData: [30, 70, 85, 80, 65, 60, 65, 95]
|
||||
},
|
||||
week: {
|
||||
xAxis: ['第1周', '第2周', '第3周', '第4周'],
|
||||
actualData: [280, 360, 320, 400],
|
||||
theoreticalData: [300, 400, 350, 450]
|
||||
},
|
||||
month: {
|
||||
xAxis: ['1月', '2月', '3月', '4月', '5月', '6月'],
|
||||
actualData: [1250, 1420, 1380, 1650, 1520, 1780],
|
||||
theoreticalData: [1350, 1500, 1450, 1700, 1600, 1850]
|
||||
}
|
||||
},
|
||||
|
||||
// 发电量预测对比数据
|
||||
powerBarData: {
|
||||
xAxis: ['上周', '本周'],
|
||||
actualData: [4899, 5200],
|
||||
forecastData: [5000, 5300]
|
||||
},
|
||||
|
||||
// 性能比分析数据
|
||||
duibiPieData: {
|
||||
num: 83,
|
||||
},
|
||||
|
||||
// 系统效率与日照强度数据
|
||||
rizhaoqiangduData: {
|
||||
xAxis: ['6:00', '7:00', '8:00', '9:00', '10:00', '11:00', '12:00', '13:00', '14:00', '15:00'],
|
||||
systemEfficiency: [14, 12, 13, 15, 12, 16, 15, 14, 13, 6],
|
||||
solarIrradiance: [100, 556, 413, 115, 510, 115, 317, 118, 14, 7]
|
||||
},
|
||||
|
||||
// 组件温度数据
|
||||
temperatureData: {
|
||||
xAxis: ['6:00', '8:00', '10:00', '12:00', '14:00', '16:00', '18:00', '20:00', '22:00'],
|
||||
componentTemp: [22, 30, 38, 28, 44, 32, 42, 25, 35],
|
||||
ambientTemp: [18, 26, 32, 24, 1, 28, 36, 22, 30]
|
||||
},
|
||||
|
||||
// 逆变器输出与汇流箱电流数据
|
||||
nibianqiData: {
|
||||
xAxis: ['00:00', '02:00', '04:00', '06:00', '08:00', '10:00', '12:00', '14:00', '16:00', '18:00', '20:00', '22:00'],
|
||||
inverterData: [40, 58, 40, 44, 61, 58, 77, 60, 78, 53, 70, 53],
|
||||
combinerBoxData: [50, 48, 44, 62, 41, 78, 57, 70, 68, 100, 60, 73]
|
||||
},
|
||||
|
||||
// 异常参数预警数据
|
||||
yujingData: [
|
||||
{
|
||||
type: 'error',
|
||||
title: '逆变器A电压异常',
|
||||
time: '今日14:32发生'
|
||||
},
|
||||
{
|
||||
type: 'warning',
|
||||
title: '组件温度过高',
|
||||
time: '今日13:45发生'
|
||||
},
|
||||
{
|
||||
type: 'info',
|
||||
title: '清理维护提醒',
|
||||
time: '3天后需要进行组件清洁'
|
||||
},
|
||||
{
|
||||
type: 'warning',
|
||||
title: '组件温度过高',
|
||||
time: '今日13:45发生'
|
||||
},
|
||||
{
|
||||
type: 'info',
|
||||
title: '清理维护提醒',
|
||||
time: '3天后需要进行组件清洁'
|
||||
},
|
||||
{
|
||||
type: 'error',
|
||||
title: '逆变器B电流异常',
|
||||
time: '今日10:22发生'
|
||||
},
|
||||
{
|
||||
type: 'warning',
|
||||
title: '散热系统效率降低',
|
||||
time: '昨日23:15发生'
|
||||
},
|
||||
{
|
||||
type: 'error',
|
||||
title: '汇流箱C通讯中断',
|
||||
time: '昨日18:30发生'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
// 模拟API调用
|
||||
const fetchData = () => {
|
||||
return new Promise((resolve) => {
|
||||
// 模拟网络延迟
|
||||
setTimeout(() => {
|
||||
resolve(mockData.value);
|
||||
}, 500);
|
||||
});
|
||||
};
|
||||
|
||||
// 页面加载时获取数据
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const data = await fetchData();
|
||||
mockData.value = data;
|
||||
} catch (error) {
|
||||
console.error('获取数据失败:', error);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.model {
|
||||
padding: 20px 15px;
|
||||
background-color: rgba(242, 248, 252, 1);
|
||||
}
|
||||
.flex-container {
|
||||
display: flex;
|
||||
}
|
||||
.flex-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between; /* 水平居中 */
|
||||
align-items: stretch; /* 拉伸子元素以填满容器高度 */
|
||||
}
|
||||
</style>
|
||||
306
src/views/pvSystem/reportManager/components/detailForm.vue
Normal file
@ -0,0 +1,306 @@
|
||||
<template>
|
||||
<div class="detail-form-container">
|
||||
<!-- 标题栏 -->
|
||||
<div class="form-header">
|
||||
<h2 class="form-title">运行参数详情表</h2>
|
||||
<div class="header-actions">
|
||||
<el-input v-model="searchKeyword" placeholder="搜索参数" prefix-icon="el-icon-search" class="search-input" />
|
||||
<el-button type="primary" class="filter-btn" @click="showFilterDialog = !showFilterDialog">
|
||||
<img src="/src/assets/demo/shaixuan.png" alt="筛选" class="btn-icon">
|
||||
筛选
|
||||
</el-button>
|
||||
<el-button type="primary" class="export-btn" @click="handleExport">
|
||||
<img src="/src/assets/demo/upload.png" alt="导出" class="btn-icon">
|
||||
导出
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 表格内容 -->
|
||||
<div class="table-wrapper">
|
||||
<el-table v-loading="loading" :data="processedTableData" style="width: 100%" border>
|
||||
<el-table-column prop="time" label="时间" align="center" />
|
||||
<el-table-column prop="irradiance" label="辐照度(W/㎡)" align="center" />
|
||||
<el-table-column prop="powerGeneration" label="发电量(KWH)" align="center" />
|
||||
<el-table-column prop="efficiency" label="效率(%)" align="center" />
|
||||
<el-table-column prop="moduleTemperature" label="组件温度(℃)" align="center" />
|
||||
<el-table-column prop="inverterTemperature" label="逆变器温度(℃)" align="center" />
|
||||
<el-table-column prop="status" label="状态" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="getStatusTagType(scope.row.status)" v-if="['success', 'warning', 'info', 'primary', 'danger'].includes(getStatusTagType(scope.row.status))">
|
||||
{{ scope.row.status }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<!-- 分页 -->
|
||||
<div class="pagination-wrapper">
|
||||
<div class="pagination-info">
|
||||
显示第 {{ (currentPage - 1) * pageSize + 1 }} 到第
|
||||
{{ Math.min(currentPage * pageSize, total) }} 条,共 {{ total }} 条记录
|
||||
</div>
|
||||
<el-pagination v-model:current-page="currentPage" v-model:page-size="pageSize" :page-sizes="[10, 20, 50, 100]"
|
||||
layout="prev, pager, next" :total="total" @size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 筛选对话框 -->
|
||||
<el-dialog v-model="showFilterDialog" title="筛选条件" width="30%">
|
||||
<div class="filter-content">
|
||||
<div class="filter-item">
|
||||
<span class="filter-label">设备状态:</span>
|
||||
<el-checkbox-group v-model="selectedStatus" class="filter-checkbox-group">
|
||||
<el-checkbox v-for="option in statusOptions" :key="option.value" :label="option.value">
|
||||
{{ option.label }}
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="resetFilter">重置</el-button>
|
||||
<el-button type="primary" @click="handleFilter">确定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted } from 'vue';
|
||||
import { ElMessage, ElDialog, ElCheckboxGroup, ElCheckbox, ElButton } from 'element-plus';
|
||||
|
||||
// 定义props
|
||||
const props = defineProps({
|
||||
tableData: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
});
|
||||
|
||||
// 搜索关键词
|
||||
const searchKeyword = ref('');
|
||||
|
||||
// 表格加载状态
|
||||
const loading = ref(false);
|
||||
|
||||
// 分页数据 - 设置为第2页,共200条记录(20页)
|
||||
const currentPage = ref(2);
|
||||
const pageSize = ref(10);
|
||||
const total = ref(200); // 模拟总条数,20页
|
||||
|
||||
// 筛选相关
|
||||
const showFilterDialog = ref(false);
|
||||
const selectedStatus = ref<string[]>([]);
|
||||
const statusOptions = [
|
||||
{ label: '正常', value: '正常' },
|
||||
{ label: '停机', value: '停机' }
|
||||
];
|
||||
|
||||
// 筛选按钮点击处理
|
||||
const handleFilter = () => {
|
||||
// 筛选逻辑已在processedTableData计算属性中实现
|
||||
showFilterDialog.value = false;
|
||||
};
|
||||
|
||||
// 重置筛选
|
||||
const resetFilter = () => {
|
||||
selectedStatus.value = [];
|
||||
};
|
||||
|
||||
|
||||
|
||||
// 计算表格数据(模拟分页、搜索和筛选)
|
||||
const processedTableData = computed(() => {
|
||||
let filteredData = [...props.tableData];
|
||||
|
||||
// 搜索逻辑
|
||||
if (searchKeyword.value) {
|
||||
filteredData = filteredData.filter(item => {
|
||||
return Object.values(item).some(val =>
|
||||
String(val).includes(searchKeyword.value)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// 状态筛选逻辑
|
||||
if (selectedStatus.value.length > 0) {
|
||||
filteredData = filteredData.filter(item =>
|
||||
selectedStatus.value.includes(item.status)
|
||||
);
|
||||
}
|
||||
|
||||
// 模拟分页逻辑
|
||||
const start = (currentPage.value - 1) * pageSize.value;
|
||||
const end = start + pageSize.value;
|
||||
return filteredData.slice(start, end);
|
||||
});
|
||||
|
||||
// 根据状态获取标签类型
|
||||
const getStatusTagType = (status: string) => {
|
||||
switch (status) {
|
||||
case '正常':
|
||||
return 'success';
|
||||
case '停机':
|
||||
return 'info';
|
||||
default:
|
||||
return 'default';
|
||||
}
|
||||
};
|
||||
|
||||
// 分页大小变化处理
|
||||
const handleSizeChange = (size) => {
|
||||
pageSize.value = size;
|
||||
// 实际应用中这里应该重新获取数据
|
||||
ElMessage.info(`每页显示 ${size} 条`);
|
||||
};
|
||||
|
||||
// 当前页变化处理
|
||||
const handleCurrentChange = (current) => {
|
||||
currentPage.value = current;
|
||||
// 实际应用中这里应该重新获取数据
|
||||
};
|
||||
|
||||
// 导出功能
|
||||
const handleExport = () => {
|
||||
ElMessage.success('导出成功');
|
||||
// 实际应用中这里应该实现真实的导出逻辑
|
||||
};
|
||||
|
||||
// 初始化数据
|
||||
onMounted(() => {
|
||||
// 模拟加载数据
|
||||
loading.value = true;
|
||||
setTimeout(() => {
|
||||
loading.value = false;
|
||||
}, 500);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.detail-form-container {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.form-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #f5f5f5;
|
||||
}
|
||||
|
||||
.form-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.filter-btn {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.btn-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-right: 5px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.table-wrapper {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
/* 筛选对话框样式 */
|
||||
.filter-content {
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.filter-item {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.filter-label {
|
||||
display: inline-block;
|
||||
width: 80px;
|
||||
text-align: right;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.filter-checkbox-group {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.pagination-wrapper {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-top: 10px;
|
||||
border-top: 1px solid #f5f5f5;
|
||||
}
|
||||
|
||||
.pagination-info {
|
||||
color: #8c8c8c;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 适配不同屏幕尺寸 */
|
||||
@media (max-width: 1200px) {
|
||||
.header-actions {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.detail-form-container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.form-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.header-actions {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.search-input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.pagination-wrapper {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 确保表格在小屏幕上可以水平滚动 */
|
||||
.table-wrapper {
|
||||
overflow-x: auto;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
</style>
|
||||
294
src/views/pvSystem/reportManager/components/fenxiLine.vue
Normal file
@ -0,0 +1,294 @@
|
||||
<template>
|
||||
<div class="chart-container">
|
||||
<div class="chart-header">
|
||||
<h2>效率分析</h2>
|
||||
<div class="chart-actions">
|
||||
<button :class="{ active: timeRange === 'hour' }" @click="changeTimeRange('hour')">小时</button>
|
||||
<button :class="{ active: timeRange === 'day' }" @click="changeTimeRange('day')">每天</button>
|
||||
<button :class="{ active: timeRange === 'week' }" @click="changeTimeRange('week')">每周</button>
|
||||
</div>
|
||||
</div>
|
||||
<!--图表内容区域 -->
|
||||
<div ref="chartRef" class="chart-content"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
// 图表DOM引用
|
||||
const chartRef = ref(null);
|
||||
// 图表实例
|
||||
let chartInstance = null;
|
||||
|
||||
// 当前时间范围状态
|
||||
const timeRange = ref('hour'); // 默认显示小时数据
|
||||
|
||||
// 定义props
|
||||
const props = defineProps({
|
||||
chartData: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
hour: {
|
||||
xAxis: ['00:00', '02:00', '04:00', '06:00', '08:00', '10:00', '12:00'],
|
||||
efficiencyData: [7, 10, 14, 17, 18, 19, 20]
|
||||
},
|
||||
day: {
|
||||
xAxis: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
|
||||
efficiencyData: [12, 15, 17, 14, 18, 16, 19]
|
||||
},
|
||||
week: {
|
||||
xAxis: ['第1周', '第2周', '第3周', '第4周', '第5周', '第6周'],
|
||||
efficiencyData: [10, 13, 16, 14, 18, 17]
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
// 切换时间范围的函数
|
||||
const changeTimeRange = (range) => {
|
||||
timeRange.value = range;
|
||||
updateChartData();
|
||||
};
|
||||
|
||||
// 更新图表数据的函数
|
||||
const updateChartData = () => {
|
||||
if (chartInstance) {
|
||||
const data = props.chartData[timeRange.value];
|
||||
chartInstance.setOption({
|
||||
xAxis: {
|
||||
data: data.xAxis
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data: data.efficiencyData
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 初始化图表
|
||||
const initChart = () => {
|
||||
if (chartRef.value && !chartInstance) {
|
||||
chartInstance = echarts.init(chartRef.value);
|
||||
}
|
||||
|
||||
const data = props.chartData[timeRange.value];
|
||||
|
||||
const option = {
|
||||
title: {
|
||||
text: '',
|
||||
show: false
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
backgroundColor: 'rgba(33,56,77,1)',
|
||||
borderColor: 'rgba(33,56,77,1)',
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
fontSize: 14
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
data: ['转换效率(%)'],
|
||||
icon:'circle',
|
||||
left: '5%', // 放在左边
|
||||
itemWidth: 8, // 调小icon宽度
|
||||
itemHeight: 8 // 调小icon高度
|
||||
},
|
||||
grid: {
|
||||
top: '20%',
|
||||
right: '4%',
|
||||
bottom: '4%',
|
||||
left: '6%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
data: data.xAxis,
|
||||
type: 'category',
|
||||
boundaryGap: true,
|
||||
axisLabel: {
|
||||
textStyle: {
|
||||
color: '#3E4954',
|
||||
fontSize: 12
|
||||
}
|
||||
},
|
||||
axisTick: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
min: 0,
|
||||
max: 25,
|
||||
interval: 5,
|
||||
axisLabel: {
|
||||
formatter: '{value}%',
|
||||
textStyle: {
|
||||
color: '#333',
|
||||
fontSize: 12
|
||||
}
|
||||
},
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#f0f0f0',
|
||||
type: 'dashed'
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '转换效率(%)',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'circle',
|
||||
symbolSize: 8,
|
||||
lineStyle: {
|
||||
width: 3,
|
||||
color: '#1890ff'
|
||||
},
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
[
|
||||
{
|
||||
offset: 0,
|
||||
color: 'rgba(24, 144, 255, 0.6)'
|
||||
},
|
||||
{
|
||||
offset: 1,
|
||||
color: 'rgba(24, 144, 255, 0.1)'
|
||||
}
|
||||
],
|
||||
false
|
||||
)
|
||||
},
|
||||
itemStyle: {
|
||||
color: '#1890ff', // 设置为实心圆
|
||||
borderWidth: 0 // 移除边框
|
||||
},
|
||||
data: data.efficiencyData
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
chartInstance.setOption(option);
|
||||
};
|
||||
|
||||
// 响应窗口大小变化
|
||||
const handleResize = () => {
|
||||
if (chartInstance) {
|
||||
chartInstance.resize();
|
||||
}
|
||||
};
|
||||
|
||||
// 生命周期钩子
|
||||
onMounted(() => {
|
||||
initChart();
|
||||
window.addEventListener('resize', handleResize);
|
||||
|
||||
// 清理函数
|
||||
return () => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
if (chartInstance) {
|
||||
chartInstance.dispose();
|
||||
chartInstance = null;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.chart-container {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
height: 400px;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.chart-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 15px 20px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.chart-header h2 {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.chart-actions button {
|
||||
background: none;
|
||||
border: 1px solid #e0e0e0;
|
||||
padding: 5px 12px;
|
||||
border-radius: 4px;
|
||||
margin-left: 8px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.chart-actions button.active {
|
||||
background-color: #1890ff;
|
||||
color: white;
|
||||
border-color: #1890ff;
|
||||
}
|
||||
|
||||
.chart-content {
|
||||
width: 100%;
|
||||
height: calc(100% - 70px);
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.chart-container {
|
||||
height: 350px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.chart-container {
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.chart-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.chart-actions {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.chart-actions button {
|
||||
margin: 0;
|
||||
flex: 1;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.chart-actions button:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
293
src/views/pvSystem/reportManager/components/itembox.vue
Normal file
@ -0,0 +1,293 @@
|
||||
<template>
|
||||
<div class="item-box">
|
||||
<div class="item-info">
|
||||
<div class="power-box">
|
||||
<div class="power-info">
|
||||
<div class="power-title">
|
||||
{{ title || '平均效率' }}
|
||||
</div>
|
||||
<div class="power-amount">{{ value || '2,456.8' }} <span>{{ unit || 'KWh' }}</span></div>
|
||||
<div class="power-growth">
|
||||
<span class="growth-value">{{ growth || '+2.5%' }}</span>
|
||||
<span class="growth-label">{{ growthLabel || '较昨日' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="power-icon" :style="{ backgroundColor: color }">
|
||||
<el-icon>
|
||||
<img :src="iconSrc" alt="">
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="itemView">
|
||||
<div class="chart-placeholder">
|
||||
<div ref="chartRef" class="chart-wrapper" style="width: 100%; height: 100%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, ref, onUnmounted, watch } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
// Props定义
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String,
|
||||
default: '平均效率'
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
default: '2,456.8'
|
||||
},
|
||||
unit: {
|
||||
type: String,
|
||||
default: 'KWh'
|
||||
},
|
||||
growth: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
growthLabel: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
power: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
iconSrc: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
chartData: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
chartType: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
});
|
||||
|
||||
// 柱状图相关
|
||||
const chartRef = ref(null);
|
||||
let chartInstance = null;
|
||||
|
||||
// 初始化柱状图
|
||||
const initChart = () => {
|
||||
if (!chartRef.value || chartInstance) return;
|
||||
|
||||
// 创建图表实例
|
||||
chartInstance = echarts.init(chartRef.value);
|
||||
|
||||
// 配置项
|
||||
const option = {
|
||||
grid: {
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
top: 0,
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: Array.from({length: props.chartData.length}, (_, i) => i + 1),
|
||||
show: false // 不显示x轴
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
show: false // 不显示y轴
|
||||
},
|
||||
series: [{
|
||||
data: props.chartData,
|
||||
type: props.chartType,
|
||||
itemStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 1, 0, 0, [
|
||||
{offset: 0, color: props.color},
|
||||
{offset: 1, color: props.color + '80'}
|
||||
])
|
||||
},
|
||||
// 根据图表类型设置不同的配置
|
||||
...(props.chartType === 'bar' ? {
|
||||
barWidth: '60%',
|
||||
barGap: 0,
|
||||
barCategoryGap: '20%'
|
||||
} : props.chartType === 'line' ? {
|
||||
smooth: true,
|
||||
symbol: 'none', // 取消拐点显示
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 1, 0, 0, [
|
||||
{offset: 0, color: props.color + '40'},
|
||||
{offset: 1, color: props.color + '20'}
|
||||
])
|
||||
}
|
||||
} : {})
|
||||
}],
|
||||
tooltip: {
|
||||
show: true,
|
||||
trigger: 'axis',
|
||||
formatter: function(params) {
|
||||
return params[0].value + '%';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 设置配置项
|
||||
chartInstance.setOption(option);
|
||||
};
|
||||
|
||||
// 监听窗口大小变化,重绘图表
|
||||
const handleResize = () => {
|
||||
if (chartInstance) {
|
||||
chartInstance.resize();
|
||||
}
|
||||
};
|
||||
|
||||
// 监听图表数据变化
|
||||
watch(() => props.chartData, () => {
|
||||
if (chartInstance) {
|
||||
chartInstance.setOption({
|
||||
xAxis: {
|
||||
data: Array.from({length: props.chartData.length}, (_, i) => i + 1)
|
||||
},
|
||||
series: [{
|
||||
data: props.chartData
|
||||
}]
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 监听图表类型和颜色变化
|
||||
watch([() => props.chartType, () => props.chartColor], () => {
|
||||
if (chartInstance) {
|
||||
initChart();
|
||||
}
|
||||
});
|
||||
|
||||
// 生命周期钩子
|
||||
onMounted(() => {
|
||||
initChart();
|
||||
window.addEventListener('resize', handleResize);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (chartInstance) {
|
||||
chartInstance.dispose();
|
||||
chartInstance = null;
|
||||
}
|
||||
window.removeEventListener('resize', handleResize);
|
||||
});
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
/* itemBox样式 */
|
||||
.item-box {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
padding: 15px 25px 0 25px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.item-info {
|
||||
margin-bottom: 16px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.power-box {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.power-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.power-title {
|
||||
font-size: 16px;
|
||||
color: #999;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.power-amount {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.power-amount span {
|
||||
font-size: 16px;
|
||||
font-weight: normal;
|
||||
color: #666;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.power-growth {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.power-icon {
|
||||
width: 33px;
|
||||
height: 55px;
|
||||
opacity: 1;
|
||||
border-radius: 7px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.growth-value {
|
||||
font-size: 14px;
|
||||
color: #13C2C2;
|
||||
}
|
||||
|
||||
.growth-label {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.itemView {
|
||||
position: relative;
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
.chart-placeholder {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* echarts图表容器样式 */
|
||||
.chart-wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* 确保在小屏幕下也能良好显示 */
|
||||
@media screen and (max-width: 1500px) {
|
||||
.item-box {
|
||||
padding: 12px 20px;
|
||||
}
|
||||
|
||||
.power-title {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.power-amount {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.power-amount span {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
251
src/views/pvSystem/reportManager/components/qushiBar.vue
Normal file
@ -0,0 +1,251 @@
|
||||
<template>
|
||||
<div class="chart-container">
|
||||
<div class="chart-header">
|
||||
<h2>效率分析</h2>
|
||||
<div class="chart-actions">
|
||||
<button :class="{ active: timeRange === 'hour' }" @click="changeTimeRange('hour')">小时</button>
|
||||
<button :class="{ active: timeRange === 'day' }" @click="changeTimeRange('day')">每天</button>
|
||||
<button :class="{ active: timeRange === 'week' }" @click="changeTimeRange('week')">每周</button>
|
||||
</div>
|
||||
</div>
|
||||
<!--图表内容区域 -->
|
||||
<div ref="chartRef" class="chart-content"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
// 图表DOM引用
|
||||
const chartRef = ref(null);
|
||||
// 图表实例
|
||||
let chartInstance = null;
|
||||
|
||||
// 当前时间范围状态
|
||||
const timeRange = ref('hour'); // 默认显示小时数据
|
||||
|
||||
// 定义props
|
||||
const props = defineProps({
|
||||
chartData: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
hour: {
|
||||
xAxis: [],
|
||||
efficiencyData: []
|
||||
},
|
||||
day: {
|
||||
xAxis: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
|
||||
efficiencyData: []
|
||||
},
|
||||
week: {
|
||||
xAxis: ['第1周', '第2周', '第3周', '第4周', '第5周', '第6周'],
|
||||
efficiencyData: []
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
// 切换时间范围的函数
|
||||
const changeTimeRange = (range) => {
|
||||
timeRange.value = range;
|
||||
updateChartData();
|
||||
};
|
||||
|
||||
// 更新图表数据的函数
|
||||
const updateChartData = () => {
|
||||
if (chartInstance) {
|
||||
const data = props.chartData[timeRange.value];
|
||||
chartInstance.setOption({
|
||||
xAxis: {
|
||||
data: data.xAxis
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data: data.efficiencyData
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 初始化图表
|
||||
const initChart = () => {
|
||||
if (chartRef.value && !chartInstance) {
|
||||
chartInstance = echarts.init(chartRef.value);
|
||||
}
|
||||
|
||||
const data = props.chartData[timeRange.value];
|
||||
|
||||
const option = {
|
||||
title: {
|
||||
text: '',
|
||||
show: false
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
backgroundColor: 'rgba(33,56,77,1)',
|
||||
borderColor: 'rgba(33,56,77,1)',
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
fontSize: 14
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
top: '20%',
|
||||
right: '4%',
|
||||
bottom: '4%',
|
||||
left: '6%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
data: data.xAxis,
|
||||
type: 'category',
|
||||
axisTick: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
max: 100,
|
||||
interval: 10,
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#f0f0f0',
|
||||
type: 'dashed'
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '转换效率(%)',
|
||||
type: 'bar',
|
||||
data: data.efficiencyData,
|
||||
showBackground: true,
|
||||
itemStyle: {
|
||||
color: 'rgba(63, 140, 255, 1)',
|
||||
borderRadius: [6, 6, 0, 0] // 为顶部两个角设置圆角
|
||||
},
|
||||
barWidth: 50,
|
||||
backgroundStyle: {
|
||||
color: 'rgba(24, 179, 245, 0.1)'
|
||||
},
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
chartInstance.setOption(option);
|
||||
};
|
||||
|
||||
// 响应窗口大小变化
|
||||
const handleResize = () => {
|
||||
if (chartInstance) {
|
||||
chartInstance.resize();
|
||||
}
|
||||
};
|
||||
|
||||
// 生命周期钩子
|
||||
onMounted(() => {
|
||||
initChart();
|
||||
window.addEventListener('resize', handleResize);
|
||||
|
||||
// 清理函数
|
||||
return () => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
if (chartInstance) {
|
||||
chartInstance.dispose();
|
||||
chartInstance = null;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.chart-container {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
height: 400px;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.chart-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 15px 20px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.chart-header h2 {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.chart-actions button {
|
||||
background: none;
|
||||
border: 1px solid #e0e0e0;
|
||||
padding: 5px 12px;
|
||||
border-radius: 4px;
|
||||
margin-left: 8px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.chart-actions button.active {
|
||||
background-color: #1890ff;
|
||||
color: white;
|
||||
border-color: #1890ff;
|
||||
}
|
||||
|
||||
.chart-content {
|
||||
width: 100%;
|
||||
height: calc(100% - 70px);
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.chart-container {
|
||||
height: 350px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.chart-container {
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.chart-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.chart-actions {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.chart-actions button {
|
||||
margin: 0;
|
||||
flex: 1;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.chart-actions button:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
361
src/views/pvSystem/reportManager/components/qushiLine.vue
Normal file
@ -0,0 +1,361 @@
|
||||
<template>
|
||||
<div class="chart-container">
|
||||
<div class="chart-header">
|
||||
<h2>组件温度(℃)</h2>
|
||||
<div class="chart-actions">
|
||||
<button :class="{ active: timeRange === 'hour' }" @click="changeTimeRange('hour')">小时</button>
|
||||
<button :class="{ active: timeRange === 'day' }" @click="changeTimeRange('day')">每天</button>
|
||||
<button :class="{ active: timeRange === 'week' }" @click="changeTimeRange('week')">每周</button>
|
||||
</div>
|
||||
</div>
|
||||
<!--图表内容区域 -->
|
||||
<div ref="chartRef" class="chart-content"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, watch } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
|
||||
// 图表DOM引用
|
||||
const chartRef = ref(null);
|
||||
// 图表实例
|
||||
let chartInstance = null;
|
||||
|
||||
// 定义颜色常量
|
||||
const ACTUAL_COLOR = '#FF8D1A';
|
||||
const THEORETICAL_COLOR = '#FF5733';
|
||||
|
||||
// 当前时间范围状态
|
||||
const timeRange = ref('hour'); // 默认显示小时数据
|
||||
|
||||
// 定义props
|
||||
const props = defineProps({
|
||||
chartData: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
hour: {
|
||||
xAxis: ['6:00', '8:00', '10:00', '12:00', '14:00', '16:00', '18:00', '20:00', '22:00'],
|
||||
componentTemp: [22, 30, 38, 28, 44, 32, 42, 25, 35],
|
||||
ambientTemp: [18, 26, 32, 24, 40, 28, 36, 22, 30]
|
||||
},
|
||||
day: {
|
||||
xAxis: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
|
||||
componentTemp: [25, 30, 35, 32, 38, 28, 30],
|
||||
ambientTemp: [20, 24, 29, 27, 32, 25, 26]
|
||||
},
|
||||
week: {
|
||||
xAxis: ['第1周', '第2周', '第3周', '第4周', '第5周', '第6周'],
|
||||
componentTemp: [28, 32, 36, 33, 30, 35],
|
||||
ambientTemp: [24, 27, 30, 28, 26, 29]
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
// 十六进制颜色转rgba工具函数
|
||||
const hexToRgba = (hex, opacity) => {
|
||||
// 移除#号
|
||||
hex = hex.replace('#', '');
|
||||
|
||||
// 解析RGB值
|
||||
const r = parseInt(hex.substring(0, 2), 16);
|
||||
const g = parseInt(hex.substring(2, 4), 16);
|
||||
const b = parseInt(hex.substring(4, 6), 16);
|
||||
|
||||
return `rgba(${r}, ${g}, ${b}, ${opacity})`;
|
||||
};
|
||||
|
||||
// 切换时间范围的函数
|
||||
const changeTimeRange = (range) => {
|
||||
timeRange.value = range;
|
||||
updateChartData();
|
||||
};
|
||||
|
||||
// 更新图表数据的函数
|
||||
const updateChartData = () => {
|
||||
if (chartInstance) {
|
||||
const data = props.chartData[timeRange.value];
|
||||
chartInstance.setOption({
|
||||
xAxis: {
|
||||
data: data.xAxis
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data: data.componentTemp
|
||||
},
|
||||
{
|
||||
data: data.ambientTemp
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
};
|
||||
// 初始化图表
|
||||
const initChart = () => {
|
||||
if (chartRef.value && !chartInstance) {
|
||||
chartInstance = echarts.init(chartRef.value);
|
||||
}
|
||||
|
||||
const data = props.chartData[timeRange.value];
|
||||
|
||||
const option = {
|
||||
title: {
|
||||
text: '', // 标题已移至HTML中
|
||||
show: false
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
backgroundColor: 'rgba(33,56,77,1)',
|
||||
borderColor: 'rgba(33,56,77,1)',
|
||||
textStyle: {
|
||||
color: '#fff',
|
||||
fontSize: 14
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
left: 'center',
|
||||
top: 4,
|
||||
itemWidth: 10,
|
||||
itemHeight: 10,
|
||||
itemGap: 25,
|
||||
icon: 'stack',
|
||||
data: ['组件温度', '逆变器温度']
|
||||
},
|
||||
grid: {
|
||||
top: '20%',
|
||||
right: '4%',
|
||||
bottom: '4%',
|
||||
left: '6%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
data: data.xAxis,
|
||||
type: 'category',
|
||||
boundaryGap: true,
|
||||
axisLabel: {
|
||||
textStyle: {
|
||||
color: '#3E4954',
|
||||
fontSize: 12
|
||||
}
|
||||
},
|
||||
axisTick: {
|
||||
show: false // 去除刻度线
|
||||
},
|
||||
},
|
||||
yAxis:
|
||||
{
|
||||
type: 'value',
|
||||
min: 0,
|
||||
max: 50,
|
||||
axisLabel: {
|
||||
formatter: '{value} ℃',
|
||||
textStyle: {
|
||||
color: '#3E4954',
|
||||
fontSize: 12
|
||||
}
|
||||
},
|
||||
axisTick: {
|
||||
show: false // 去除刻度线
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#f0f0f0',
|
||||
type: 'dashed'
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '组件温度',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
showSymbol: false, // 取消点显示
|
||||
lineStyle: {
|
||||
width: 2, // 线条宽度,单位是 px
|
||||
color: ACTUAL_COLOR
|
||||
},
|
||||
// 填充颜色设置
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
[
|
||||
{
|
||||
offset: 0,
|
||||
color: hexToRgba(ACTUAL_COLOR, 0.4) // 使用ACTUAL_COLOR并设置透明度
|
||||
},
|
||||
{
|
||||
offset: 0.9,
|
||||
color: hexToRgba(ACTUAL_COLOR, 0) // 使用ACTUAL_COLOR并设置透明度为0
|
||||
}
|
||||
],
|
||||
false
|
||||
),
|
||||
shadowColor: 'rgba(0, 0, 0, 0.1)'
|
||||
},
|
||||
// 设置拐点颜色以及边框
|
||||
itemStyle: {
|
||||
color: ACTUAL_COLOR
|
||||
},
|
||||
data: data.componentTemp
|
||||
},
|
||||
{
|
||||
name: '逆变器温度',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
showSymbol: false, // 取消点显示
|
||||
lineStyle: {
|
||||
width: 2, // 线条宽度,单位是 px
|
||||
color: THEORETICAL_COLOR
|
||||
},
|
||||
// 填充颜色设置
|
||||
areaStyle: {
|
||||
color: new echarts.graphic.LinearGradient(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
[
|
||||
{
|
||||
offset: 0,
|
||||
color: hexToRgba(THEORETICAL_COLOR, 0.4) // 使用THEORETICAL_COLOR并设置透明度
|
||||
},
|
||||
{
|
||||
offset: 0.9,
|
||||
color: hexToRgba(THEORETICAL_COLOR, 0) // 使用THEORETICAL_COLOR并设置透明度为0
|
||||
}
|
||||
],
|
||||
false
|
||||
),
|
||||
shadowColor: 'rgba(0, 0, 0, 0.1)'
|
||||
},
|
||||
itemStyle: {
|
||||
color: THEORETICAL_COLOR
|
||||
},
|
||||
data: data.ambientTemp
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
chartInstance.setOption(option);
|
||||
};
|
||||
|
||||
// 响应窗口大小变化
|
||||
const handleResize = () => {
|
||||
if (chartInstance) {
|
||||
chartInstance.resize();
|
||||
}
|
||||
};
|
||||
|
||||
// 生命周期钩子
|
||||
onMounted(() => {
|
||||
initChart();
|
||||
window.addEventListener('resize', handleResize);
|
||||
|
||||
// 清理函数
|
||||
return () => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
if (chartInstance) {
|
||||
chartInstance.dispose();
|
||||
chartInstance = null;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.chart-container {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
height: 400px;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.chart-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 15px 20px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.chart-header h2 {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.chart-actions button {
|
||||
background: none;
|
||||
border: 1px solid #e0e0e0;
|
||||
padding: 5px 12px;
|
||||
border-radius: 4px;
|
||||
margin-left: 8px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.chart-actions button.active {
|
||||
background-color: #1890ff;
|
||||
color: white;
|
||||
border-color: #1890ff;
|
||||
}
|
||||
|
||||
.chart-content {
|
||||
width: 100%;
|
||||
height: calc(100% - 54px);
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.chart-container {
|
||||
height: 350px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.chart-container {
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.chart-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.chart-actions {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.chart-actions button {
|
||||
margin: 0;
|
||||
flex: 1;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.chart-actions button:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.model {
|
||||
padding: 20px;
|
||||
background-color: rgba(242, 248, 252, 1);
|
||||
}
|
||||
</style>
|
||||
264
src/views/pvSystem/reportManager/components/statusPie.vue
Normal file
@ -0,0 +1,264 @@
|
||||
<template>
|
||||
<!-- 图表容器 -->
|
||||
<div class="chart-container">
|
||||
<div class="chart-header">
|
||||
<h2>设备状态分布</h2>
|
||||
<div class="chart-actions">
|
||||
<img src="@/assets/demo/more.png" alt="">
|
||||
</div>
|
||||
</div>
|
||||
<!-- 使用ref引用以便在JS中获取该DOM元素来渲染图表 -->
|
||||
<div ref="chartRef" class="chart-content" />
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import * as echarts from "echarts";
|
||||
// 导入Vue的ref和onMounted响应式API和生命周期钩子
|
||||
import { ref, onMounted } from "vue";
|
||||
|
||||
// 创建图表容器的ref引用,用于获取DOM元素
|
||||
const chartRef = ref(null);
|
||||
|
||||
// 定义props
|
||||
const props = defineProps({
|
||||
chartData: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
"正常运行": { value: 40, color: '#43CF7C' },
|
||||
"轻微异常": { value: 30, color: '#FFC300' },
|
||||
"故障": { value: 20, color: '#FF5733' },
|
||||
"维护中": { value: 10, color: '#2A82E4' }
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
// 组件挂载后执行的初始化逻辑
|
||||
onMounted(() => {
|
||||
// 确保DOM元素已渲染完成
|
||||
if (chartRef.value) {
|
||||
// 使用ECharts初始化图表实例
|
||||
var myChart3 = echarts.init(chartRef.value);
|
||||
|
||||
// 使用从props获取的数据
|
||||
const statusData = props.chartData;
|
||||
|
||||
// 计算数据总和
|
||||
const totalSum = Object.values(statusData).reduce((sum, item) => sum + item.value, 0);
|
||||
|
||||
// 定义空白占位区域的样式配置
|
||||
const placeItemStyle = {
|
||||
label: { show: false }, // 不显示标签
|
||||
labelLine: { show: false }, // 不显示标签线
|
||||
itemStyle: { color: "#cbccd0" }, // 设置占位区域的颜色为灰色
|
||||
emphasis: { // 鼠标悬停时的样式
|
||||
label: { show: false },
|
||||
labelLine: { show: false }
|
||||
}
|
||||
};
|
||||
|
||||
// 设置图表配置项
|
||||
myChart3.setOption({
|
||||
// 提示框配置(默认显示)
|
||||
tooltip: {
|
||||
formatter: function (params) {
|
||||
// 计算当前数据占总和的百分比
|
||||
const percentage = ((params.value / totalSum) * 100).toFixed(1);
|
||||
return `${params.name}: ${percentage}%`;
|
||||
}
|
||||
},
|
||||
// 图例配置
|
||||
legend: {
|
||||
top: 'middle',
|
||||
orient: 'vertical',
|
||||
right:'20%', // 调整图例位置,使其更靠近左侧
|
||||
data: Object.keys(statusData), // 使用props数据的键作为图例数据项
|
||||
// 图例格式化函数,用于自定义图例的显示内容
|
||||
formatter: function (name) {
|
||||
// 计算当前状态占总和的百分比
|
||||
const percentage = ((statusData[name].value / totalSum) * 100).toFixed(1);
|
||||
// 返回格式化后的图例文本(状态名称+百分比)
|
||||
return `${name} ${percentage}%`;
|
||||
}
|
||||
},
|
||||
// 图表系列配置(多个环形图组成的嵌套饼图)
|
||||
series: [
|
||||
// 外层环:正常运行状态(绿色)
|
||||
{
|
||||
name: "设备状态", // 系列名称
|
||||
label: {
|
||||
show: false
|
||||
},
|
||||
type: "pie", // 图表类型为饼图
|
||||
center: ['30%', '50%'], // 饼图中心位置,往左移
|
||||
radius: ["55%", "60%"], // 设置圆环的内半径和外半径(占容器百分比)
|
||||
zlevel: 1, // 设置图层级别,值越大越在顶层
|
||||
hoverAnimation: false, // 禁用鼠标悬停动画效果
|
||||
data: [
|
||||
{ // 正常运行部分
|
||||
value: statusData["正常运行"].value, // 使用实际数值
|
||||
name: "正常运行", // 名称
|
||||
itemStyle: { color: '#43CF7C' } // 设置绿色
|
||||
},
|
||||
{ // 空白占位部分(使圆环显示为圆弧)
|
||||
value: totalSum - statusData["正常运行"].value, // 计算值,使总百分比为总和
|
||||
name: "", // 无名称
|
||||
...placeItemStyle // 应用占位区域样式
|
||||
}
|
||||
]
|
||||
},
|
||||
// 第二环:轻微异常状态(黄色)
|
||||
{
|
||||
name: "设备状态",
|
||||
label: {
|
||||
show: false
|
||||
},
|
||||
type: "pie",
|
||||
center: ['30%', '50%'], // 饼图中心位置,往左移
|
||||
radius: ["45%", "50%"], // 比外层环稍小
|
||||
zlevel: 2,
|
||||
hoverAnimation: false,
|
||||
data: [
|
||||
{ // 轻微异常部分
|
||||
value: statusData["轻微异常"].value,
|
||||
name: "轻微异常",
|
||||
itemStyle: { color: '#FFC300' } // 设置黄色
|
||||
},
|
||||
{ // 空白占位部分
|
||||
value: totalSum - statusData["轻微异常"].value,
|
||||
name: "",
|
||||
...placeItemStyle
|
||||
}
|
||||
]
|
||||
},
|
||||
// 第三环:故障状态(红色)
|
||||
{
|
||||
name: "设备状态",
|
||||
label: {
|
||||
show: false
|
||||
},
|
||||
type: "pie",
|
||||
center: ['30%', '50%'], // 饼图中心位置,往左移
|
||||
radius: ["35%", "40%"], // 更小的环
|
||||
zlevel: 3,
|
||||
hoverAnimation: false,
|
||||
data: [
|
||||
{ // 故障部分
|
||||
value: statusData["故障"].value,
|
||||
name: "故障",
|
||||
itemStyle: { color: '#FF5733' } // 设置红色
|
||||
},
|
||||
{ // 空白占位部分
|
||||
value: totalSum - statusData["故障"].value,
|
||||
name: "",
|
||||
...placeItemStyle
|
||||
}
|
||||
]
|
||||
},
|
||||
// 内层环:维护中状态(蓝色)
|
||||
{
|
||||
name: "设备状态",
|
||||
label: {
|
||||
show: false
|
||||
},
|
||||
type: "pie",
|
||||
center: ['30%', '50%'], // 饼图中心位置,往左移
|
||||
radius: ["25%", "30%"], // 最内层环
|
||||
zlevel: 4,
|
||||
hoverAnimation: false,
|
||||
data: [
|
||||
{ // 维护中部分
|
||||
value: statusData["维护中"].value,
|
||||
name: "维护中",
|
||||
itemStyle: { color: '#2A82E4' } // 设置蓝色
|
||||
},
|
||||
{ // 空白占位部分
|
||||
value: totalSum - statusData["维护中"].value,
|
||||
name: "",
|
||||
...placeItemStyle
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
// 响应窗口大小变化
|
||||
const handleResize = () => {
|
||||
if (myChart3) {
|
||||
myChart3.resize();
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('resize', handleResize);
|
||||
|
||||
// 清理函数
|
||||
return () => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
if (myChart3) {
|
||||
myChart3.dispose();
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
/* 设置图表容器的尺寸 */
|
||||
.chart-container {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
height: 400px;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.chart-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 15px 20px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.chart-header h2 {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.chart-actions img {
|
||||
cursor: pointer;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.chart-content {
|
||||
width: 100%;
|
||||
height: calc(100% - 70px);
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.chart-container {
|
||||
height: 350px;
|
||||
}
|
||||
|
||||
.chart-content {
|
||||
height: calc(100% - 70px);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.chart-container {
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.chart-content {
|
||||
height: calc(100% - 70px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
268
src/views/pvSystem/reportManager/index.vue
Normal file
@ -0,0 +1,268 @@
|
||||
<template>
|
||||
<div class="model">
|
||||
<!-- 标题栏 -->
|
||||
<el-row :gutter="24" class="title-row">
|
||||
<el-col :span="12">
|
||||
<TitleComponent title="运行参数报表" subtitle="查看和分析光伏系统的各项运行参数的日、月、年报表数据" />
|
||||
</el-col>
|
||||
<!-- 表单控件区域:使用flex布局确保同排显示 -->
|
||||
<el-col :span="12" style="display: flex; justify-content: flex-end; align-items: center;">
|
||||
<div class="form-controls">
|
||||
<div class="control-item">
|
||||
<el-select placeholder="请选择时间" style="width: 100%;">
|
||||
<el-option label="今天" value="all"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="control-item">
|
||||
<el-select placeholder="请选择报表" style="width: 100%;">
|
||||
<el-option label="日报表" value="all"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="control-item date-range">
|
||||
<el-date-picker v-model="value1" type="daterange" range-separator="-" start-placeholder="开始"
|
||||
end-placeholder="结束" style="width: 100%;" />
|
||||
</div>
|
||||
<div class="control-item">
|
||||
<el-button @click="refreshData">
|
||||
刷新数据
|
||||
<el-icon class="el-icon--right">
|
||||
<Refresh />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="control-item">
|
||||
<el-button type="primary">
|
||||
导出数据
|
||||
<el-icon class="el-icon--right">
|
||||
<UploadFilled />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!-- 数据展示-->
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="6">
|
||||
<itembox title="总发电量" value="2,456.8" unit="KWh" growth="+2.5%" growthLabel="较昨日" color="#186DF5"
|
||||
chartType="bar" power="" icon-src="/src/assets/demo/shandian.png"
|
||||
:chartData="[30, 50, 40, 60, 80, 70, 100, 90, 85, 75, 65, 55]"></itembox>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<itembox title="平均效率" value="18.7" unit="%" growth="+2.5%" growthLabel="较昨日" color="#00B87A"
|
||||
chartType="line" icon-src="/src/assets/demo/huojian.png"
|
||||
:chartData="[30, 50, 40, 60, 80, 70, 100, 90, 85, 75, 65, 55]"></itembox>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<itembox title="设备温度" value="43.5" unit="℃" growth="+2.5%" growthLabel="较昨日" color="#FFC300"
|
||||
chartType="line" icon-src="/src/assets/demo/wendu.png"
|
||||
:chartData="[30, 50, 40, 60, 80, 70, 100, 90, 85, 75, 65, 55]"></itembox>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<itembox title="系统可用性" value="18.7" unit="%" growth="+2.5%" growthLabel="较昨日" color="#7948EA"
|
||||
chartType="line" icon-src="/src/assets/demo/use.png"
|
||||
:chartData="[30, 50, 40, 60, 80, 70, 100, 90, 85, 75, 65, 55]"></itembox>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!-- 第一行图表 -->
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="17">
|
||||
<qushiBar :chartData="timeRangeData"></qushiBar>
|
||||
</el-col>
|
||||
<el-col :span="7">
|
||||
<statusPie :chartData="statusPieData"></statusPie>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!-- 第二行图表 -->
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="17">
|
||||
<qushiLine :chartData="temperatureData"></qushiLine>
|
||||
</el-col>
|
||||
<el-col :span="7">
|
||||
<fenxiLine></fenxiLine>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<!-- 详情图表 -->
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="24">
|
||||
<detailForm :tableData="tableData"></detailForm>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>// import TitleComponent from '@/views/demo/components/TitleComponent.vue';
|
||||
import TitleComponent from '@/components/TitleComponent/index.vue';
|
||||
import qushiBar from '@/views/pvSystem/reportManager/components/qushiBar.vue';
|
||||
import qushiLine from '@/views/pvSystem/reportManager/components/qushiLine.vue';
|
||||
import itembox from '@/views/pvSystem/reportManager/components/itembox.vue';
|
||||
import fenxiLine from '@/views/pvSystem/reportManager/components/fenxiLine.vue';
|
||||
import statusPie from '@/views/pvSystem/reportManager/components/statusPie.vue';
|
||||
import detailForm from '@/views/pvSystem/reportManager/components/detailForm.vue';
|
||||
import { ref } from 'vue';
|
||||
|
||||
// 日期范围选择器数据绑定
|
||||
const value1 = ref([]);
|
||||
|
||||
// mock数据定义
|
||||
const timeRangeData = ref({
|
||||
hour: {
|
||||
xAxis: ['00:00', '02:00', '04:00', '06:00', '08:00', '10:00', '12:00'],
|
||||
efficiencyData: [7, 10, 14, 17, 18, 19, 20]
|
||||
},
|
||||
day: {
|
||||
xAxis: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
|
||||
efficiencyData: [12, 15, 17, 14, 18, 16, 19]
|
||||
},
|
||||
week: {
|
||||
xAxis: ['第1周', '第2周', '第3周', '第4周', '第5周', '第6周'],
|
||||
efficiencyData: [10, 13, 16, 14, 18, 17]
|
||||
}
|
||||
});
|
||||
|
||||
const temperatureData = ref({
|
||||
hour: {
|
||||
xAxis: ['6:00', '8:00', '10:00', '12:00', '14:00', '16:00', '18:00', '20:00', '22:00'],
|
||||
componentTemp: [22, 30, 38, 28, 44, 32, 42, 25, 35],
|
||||
ambientTemp: [18, 26, 32, 24, 40, 28, 36, 22, 30]
|
||||
},
|
||||
day: {
|
||||
xAxis: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
|
||||
componentTemp: [25, 30, 35, 32, 38, 28, 30],
|
||||
ambientTemp: [20, 24, 29, 27, 32, 25, 26]
|
||||
},
|
||||
week: {
|
||||
xAxis: ['第1周', '第2周', '第3周', '第4周', '第5周', '第6周'],
|
||||
componentTemp: [28, 32, 36, 33, 30, 35],
|
||||
ambientTemp: [24, 27, 30, 28, 26, 29]
|
||||
}
|
||||
});
|
||||
|
||||
const statusPieData = ref({
|
||||
"正常运行": { value: 40, color: '#43CF7C' },
|
||||
"轻微异常": { value: 30, color: '#FFC300' },
|
||||
"故障": { value: 20, color: '#FF5733' },
|
||||
"维护中": { value: 10, color: '#2A82E4' }
|
||||
});
|
||||
|
||||
const fenxiLineData = ref({
|
||||
hour: {
|
||||
xAxis: ['00:00', '02:00', '04:00', '06:00', '08:00', '10:00', '12:00'],
|
||||
efficiencyData: [7, 10, 14, 17, 18, 19, 20]
|
||||
},
|
||||
day: {
|
||||
xAxis: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
|
||||
efficiencyData: [12, 15, 17, 14, 18, 16, 19]
|
||||
},
|
||||
week: {
|
||||
xAxis: ['第1周', '第2周', '第3周', '第4周', '第5周', '第6周'],
|
||||
efficiencyData: [10, 13, 16, 14, 18, 17]
|
||||
}
|
||||
});
|
||||
|
||||
const tableData = ref([
|
||||
{ time: '00:00', irradiance: 0, powerGeneration: 0.0, efficiency: 24.5, moduleTemperature: 23.5, inverterTemperature: 21.2, status: '停机' },
|
||||
{ time: '08:00', irradiance: 12.5, powerGeneration: 17.2, efficiency: 28.1, moduleTemperature: 26.3, inverterTemperature: 20.3, status: '正常' },
|
||||
{ time: '12:00', irradiance: 98.0, powerGeneration: 13.1, efficiency: 46.2, moduleTemperature: 23.5, inverterTemperature: 21.2, status: '正常' },
|
||||
{ time: '15:00', irradiance: 65, powerGeneration: 16.8, efficiency: 31.1, moduleTemperature: 23.5, inverterTemperature: 21.2, status: '停机' },
|
||||
{ time: '20:00', irradiance: 0, powerGeneration: 0.0, efficiency: 24.5, moduleTemperature: 23.5, inverterTemperature: 21.2, status: '停机' },
|
||||
{ time: '01:00', irradiance: 0, powerGeneration: 0.0, efficiency: 22.8, moduleTemperature: 21.5, inverterTemperature: 19.8, status: '停机' },
|
||||
{ time: '02:00', irradiance: 0, powerGeneration: 0.0, efficiency: 23.1, moduleTemperature: 22.1, inverterTemperature: 20.5, status: '停机' },
|
||||
{ time: '03:00', irradiance: 0, powerGeneration: 0.0, efficiency: 22.9, moduleTemperature: 21.8, inverterTemperature: 19.9, status: '停机' },
|
||||
{ time: '04:00', irradiance: 0, powerGeneration: 0.0, efficiency: 23.0, moduleTemperature: 22.0, inverterTemperature: 20.2, status: '停机' },
|
||||
{ time: '05:00', irradiance: 0, powerGeneration: 0.0, efficiency: 23.2, moduleTemperature: 22.3, inverterTemperature: 20.7, status: '停机' },
|
||||
{ time: '06:00', irradiance: 0, powerGeneration: 0.0, efficiency: 23.5, moduleTemperature: 22.6, inverterTemperature: 21.0, status: '停机' },
|
||||
{ time: '07:00', irradiance: 0, powerGeneration: 0.0, efficiency: 23.8, moduleTemperature: 22.9, inverterTemperature: 21.2, status: '停机' },
|
||||
{ time: '09:00', irradiance: 35.2, powerGeneration: 18.5, efficiency: 30.2, moduleTemperature: 28.5, inverterTemperature: 22.6, status: '正常' },
|
||||
{ time: '10:00', irradiance: 65.8, powerGeneration: 15.3, efficiency: 35.6, moduleTemperature: 32.8, inverterTemperature: 24.5, status: '正常' },
|
||||
{ time: '11:00', irradiance: 82.5, powerGeneration: 14.2, efficiency: 42.3, moduleTemperature: 38.2, inverterTemperature: 28.3, status: '正常' },
|
||||
{ time: '13:00', irradiance: 95.3, powerGeneration: 13.8, efficiency: 45.8, moduleTemperature: 42.6, inverterTemperature: 31.5, status: '正常' },
|
||||
{ time: '14:00', irradiance: 88.7, powerGeneration: 14.1, efficiency: 44.2, moduleTemperature: 40.8, inverterTemperature: 30.2, status: '正常' },
|
||||
{ time: '16:00', irradiance: 72.5, powerGeneration: 15.8, efficiency: 38.6, moduleTemperature: 36.4, inverterTemperature: 27.1, status: '正常' },
|
||||
{ time: '17:00', irradiance: 58.2, powerGeneration: 16.3, efficiency: 34.9, moduleTemperature: 33.2, inverterTemperature: 25.3, status: '正常' },
|
||||
{ time: '18:00', irradiance: 42.8, powerGeneration: 17.5, efficiency: 31.4, moduleTemperature: 30.1, inverterTemperature: 23.5, status: '正常' },
|
||||
{ time: '19:00', irradiance: 25.3, powerGeneration: 18.2, efficiency: 29.1, moduleTemperature: 27.6, inverterTemperature: 21.8, status: '正常' },
|
||||
{ time: '21:00', irradiance: 0, powerGeneration: 0.0, efficiency: 24.8, moduleTemperature: 23.8, inverterTemperature: 21.5, status: '停机' },
|
||||
{ time: '22:00', irradiance: 0, powerGeneration: 0.0, efficiency: 25.1, moduleTemperature: 24.1, inverterTemperature: 21.8, status: '停机' },
|
||||
{ time: '23:00', irradiance: 0, powerGeneration: 0.0, efficiency: 25.3, moduleTemperature: 24.3, inverterTemperature: 22.0, status: '停机' }
|
||||
]);
|
||||
|
||||
// 模拟API调用函数
|
||||
const fetchData = async () => {
|
||||
// 模拟网络延迟
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
resolve({
|
||||
timeRangeData: timeRangeData.value,
|
||||
temperatureData: temperatureData.value,
|
||||
statusPieData: statusPieData.value,
|
||||
fenxiLineData: fenxiLineData.value,
|
||||
tableData: tableData.value
|
||||
});
|
||||
}, 500);
|
||||
});
|
||||
};
|
||||
|
||||
// 初始化时获取数据
|
||||
fetchData().then(data => {
|
||||
// 这里可以根据需要更新数据状态
|
||||
console.log('模拟接口数据获取成功:', data);
|
||||
});
|
||||
|
||||
// 刷新数据函数
|
||||
const refreshData = async () => {
|
||||
const newData = await fetchData();
|
||||
// 更新数据状态
|
||||
timeRangeData.value = newData.timeRangeData;
|
||||
temperatureData.value = newData.temperatureData;
|
||||
statusPieData.value = newData.statusPieData;
|
||||
fenxiLineData.value = newData.fenxiLineData;
|
||||
tableData.value = newData.tableData;
|
||||
};
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
.model {
|
||||
padding: 20px 15px;
|
||||
background-color: rgba(242, 248, 252, 1);
|
||||
}
|
||||
|
||||
.title-row {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.el-row {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.el-row:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.form-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
width: 100%;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.control-item {
|
||||
flex: 0 0 auto;
|
||||
width: 130px;
|
||||
}
|
||||
|
||||
.control-item.date-range {
|
||||
flex: 0 0 auto;
|
||||
width: 260px;
|
||||
}
|
||||
|
||||
/* 确保按钮在小尺寸时也能正常显示 */
|
||||
.control-item .el-button {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* 优化表单控件的宽度和间距 */
|
||||
.el-select,
|
||||
.el-date-picker {
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
311
src/views/pvSystem/siteOverview/components/ChartBox.vue
Normal file
@ -0,0 +1,311 @@
|
||||
<template>
|
||||
<div class="chart-container">
|
||||
<!-- 图表标题和时间范围选择器 -->
|
||||
<div class="chart-header">
|
||||
<h2>功率与效率趋势</h2>
|
||||
<div class="chart-actions">
|
||||
<button @click="timeRange = 'day'" :class="{ active: timeRange === 'day' }">今日</button>
|
||||
<button @click="timeRange = 'week'" :class="{ active: timeRange === 'week' }">本周</button>
|
||||
<button @click="timeRange = 'month'" :class="{ active: timeRange === 'month' }">本月</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 图表内容区域 -->
|
||||
<div ref="chartRef" class="chart-content"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, watch } from 'vue';
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
// 图表DOM引用
|
||||
const chartRef = ref(null);
|
||||
// 时间范围状态
|
||||
const timeRange = ref('day');
|
||||
// 图表实例
|
||||
let chartInstance = null;
|
||||
|
||||
// 定义颜色常量
|
||||
const POWER_COLOR = 'rgba(42, 130, 228, 1)';
|
||||
const EFFICIENCY_COLOR = 'rgba(67, 207, 124, 1)';
|
||||
|
||||
// 生成指定范围内的随机数(用于模拟数据)
|
||||
const getRandomValue = (min, max) => {
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
};
|
||||
|
||||
// 生成指定范围内的随机浮点数(用于效率数据)
|
||||
const getRandomFloat = (min, max, decimalPlaces = 1) => {
|
||||
const value = Math.random() * (max - min) + min;
|
||||
return parseFloat(value.toFixed(decimalPlaces));
|
||||
};
|
||||
|
||||
// 获取当前月份的天数
|
||||
const getDaysInCurrentMonth = () => {
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
const month = now.getMonth() + 1; // 月份从0开始,所以+1
|
||||
return new Date(year, month, 0).getDate();
|
||||
};
|
||||
|
||||
// 根据时间范围返回对应的数据
|
||||
const getChartData = () => {
|
||||
if (timeRange.value === 'day') {
|
||||
return {
|
||||
xAxis: ['00:00', '03:00', '06:00', '09:00', '12:00', '15:00', '18:00', '21:00'],
|
||||
powerData: [320, 380, 350, 420, 580, 630, 550, 480],
|
||||
efficiencyData: [85.2, 86.5, 87.1, 88.3, 89.5, 89.2, 88.7, 88.1]
|
||||
};
|
||||
} else if (timeRange.value === 'week') {
|
||||
return {
|
||||
xAxis: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
|
||||
powerData: [4200, 4800, 5100, 4900, 5300, 3800, 3200],
|
||||
efficiencyData: [86.2, 87.5, 88.1, 87.8, 89.0, 88.5, 87.9]
|
||||
};
|
||||
} else {
|
||||
// 本月数据 - 按天显示
|
||||
const daysInMonth = getDaysInCurrentMonth();
|
||||
const xAxis = [];
|
||||
const powerData = [];
|
||||
const efficiencyData = [];
|
||||
|
||||
// 生成每天的数据
|
||||
for (let i = 1; i <= daysInMonth; i++) {
|
||||
xAxis.push(`${i}日`);
|
||||
// 生成合理范围内的功率数据(10000-25000之间)
|
||||
powerData.push(getRandomValue(10000, 25000));
|
||||
// 生成合理范围内的效率数据(85-90之间,保留1位小数)
|
||||
efficiencyData.push(getRandomFloat(85, 90));
|
||||
}
|
||||
|
||||
return {
|
||||
xAxis,
|
||||
powerData,
|
||||
efficiencyData
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// 初始化图表
|
||||
const initChart = () => {
|
||||
if (chartRef.value && !chartInstance) {
|
||||
chartInstance = echarts.init(chartRef.value);
|
||||
}
|
||||
|
||||
const data = getChartData();
|
||||
|
||||
const option = {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: { type: 'cross' }
|
||||
},
|
||||
legend: {
|
||||
data: ['总功率(kW)', '平均效率(%)'],
|
||||
top: 0
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: data.xAxis,
|
||||
// 当月天数较多时,优化X轴标签显示
|
||||
axisLabel: {
|
||||
interval: timeRange.value === 'month' ? 'auto' : 0,
|
||||
rotate: timeRange.value === 'month' ? 45 : 0
|
||||
}
|
||||
},
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
name: '总功率(kW)',
|
||||
axisLabel: {
|
||||
formatter: '{value}'
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'value',
|
||||
name: '平均效率(%)',
|
||||
min: 80,
|
||||
max: 95,
|
||||
axisLabel: {
|
||||
formatter: '{value}%'
|
||||
}
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: '总功率(kW)',
|
||||
type: 'line',
|
||||
data: data.powerData,
|
||||
smooth: true,
|
||||
lineStyle: {
|
||||
width: 3,
|
||||
color: POWER_COLOR
|
||||
},
|
||||
symbol: 'circle',
|
||||
symbolSize: 8,
|
||||
itemStyle: {
|
||||
color: POWER_COLOR
|
||||
},
|
||||
markPoint: {
|
||||
data: [
|
||||
{ type: 'max', name: '最大值' },
|
||||
{ type: 'min', name: '最小值' }
|
||||
],
|
||||
itemStyle: {
|
||||
color: POWER_COLOR
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: '平均效率(%)',
|
||||
type: 'line',
|
||||
yAxisIndex: 1,
|
||||
data: data.efficiencyData,
|
||||
smooth: true,
|
||||
lineStyle: {
|
||||
width: 3,
|
||||
type: 'dashed',
|
||||
color: EFFICIENCY_COLOR
|
||||
},
|
||||
symbol: 'diamond',
|
||||
symbolSize: 8,
|
||||
itemStyle: {
|
||||
color: EFFICIENCY_COLOR
|
||||
},
|
||||
markPoint: {
|
||||
data: [
|
||||
{ type: 'max', name: '最大值' },
|
||||
{ type: 'min', name: '最小值' }
|
||||
],
|
||||
itemStyle: {
|
||||
color: EFFICIENCY_COLOR
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
chartInstance.setOption(option);
|
||||
};
|
||||
|
||||
// 响应窗口大小变化
|
||||
const handleResize = () => {
|
||||
if (chartInstance) {
|
||||
chartInstance.resize();
|
||||
}
|
||||
};
|
||||
|
||||
// 生命周期钩子
|
||||
onMounted(() => {
|
||||
initChart();
|
||||
window.addEventListener('resize', handleResize);
|
||||
|
||||
// 清理函数
|
||||
return () => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
if (chartInstance) {
|
||||
chartInstance.dispose();
|
||||
chartInstance = null;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// 监听时间范围变化,更新图表
|
||||
watch(timeRange, () => {
|
||||
initChart();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.chart-container {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
overflow: hidden;
|
||||
height: 400px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.chart-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 15px 20px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.chart-header h2 {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.chart-actions button {
|
||||
background: none;
|
||||
border: 1px solid #e0e0e0;
|
||||
padding: 5px 12px;
|
||||
border-radius: 4px;
|
||||
margin-left: 8px;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.chart-actions button.active {
|
||||
background-color: #1890ff;
|
||||
color: white;
|
||||
border-color: #1890ff;
|
||||
}
|
||||
|
||||
.chart-content {
|
||||
width: 100%;
|
||||
height: calc(100% - 54px);
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.chart-container {
|
||||
height: 350px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.chart-container {
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.chart-header {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.chart-actions {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.chart-actions button {
|
||||
margin: 0;
|
||||
flex: 1;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.chart-actions button:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.model {
|
||||
padding: 20px;
|
||||
background-color: rgba(242, 248, 252, 1);
|
||||
}
|
||||
</style>
|
||||
210
src/views/pvSystem/siteOverview/components/gaojing.vue
Normal file
@ -0,0 +1,210 @@
|
||||
<template>
|
||||
<el-card shadow="never" style="border-radius: 10px;">
|
||||
<div style="margin-bottom: 20px;display: flex;align-items: center;justify-content: right;">
|
||||
<span style="margin-right: 5px;color: rgba(113, 128, 150, 1);font-size: 14px;">
|
||||
查看全部告警信息
|
||||
</span>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="8" height="8"
|
||||
viewBox="0 0 8 8" fill="none">
|
||||
<path
|
||||
d="M5.94575 4.15722C5.94975 4.14947 5.95624 4.14298 5.95975 4.13497C6.0285 3.99197 6.00599 3.81697 5.88425 3.70197L3.1245 1.09172C2.97399 0.949466 2.73676 0.956216 2.59425 1.10648C2.45199 1.25698 2.4585 1.49422 2.60901 1.63673L5.08526 3.97922L2.61875 6.35647C2.46975 6.50021 2.46525 6.73746 2.60901 6.88672C2.6825 6.96321 2.78076 7.00148 2.87901 7.00148C2.97276 7.00148 3.06651 6.96648 3.13926 6.89648L5.87401 4.26073C5.87927 4.25547 5.88125 4.24823 5.88651 4.24274C5.89052 4.23899 5.89476 4.23623 5.89875 4.23224C5.92 4.20997 5.93124 4.18272 5.94575 4.15722Z"
|
||||
fill="#718096">
|
||||
</path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="notice">
|
||||
<div class="item">
|
||||
<div class="icon">
|
||||
<img src="/assets/zgjxx.png" alt="">
|
||||
</div>
|
||||
<div class="content">
|
||||
<p>总告警信息</p>
|
||||
<p style="color: rgba(42, 130, 228, 1);">1,921条</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="icon">
|
||||
<img src="/assets/ycl.png" alt="">
|
||||
</div>
|
||||
<div class="content">
|
||||
<p>已处理</p>
|
||||
<p style="color:rgba(0, 184, 122, 1);">500条</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="icon">
|
||||
<img src="/assets/zzcl.png" alt="">
|
||||
</div>
|
||||
<div class="content">
|
||||
<p>正在处理</p>
|
||||
<p style="color: rgba(255, 141, 26, 1);">200条</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div class="icon">
|
||||
<img src="/assets/wcl.png" alt="">
|
||||
</div>
|
||||
<div class="content">
|
||||
<p>未处理</p>
|
||||
<p style="color: rgba(227, 39, 39, 1);">1,221</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content">
|
||||
<div class="list">
|
||||
<div class="item waring">
|
||||
<div class="left">
|
||||
<p class="title">
|
||||
逆变器温度过高
|
||||
</p>
|
||||
<p class="text">INV-2023-003 温度达到52℃,超过阈值48℃</p>
|
||||
</div>
|
||||
<div class="right">
|
||||
<div class="time">
|
||||
10分钟前
|
||||
</div>
|
||||
<el-text type="warning" size="small">正在处理</el-text>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item danger">
|
||||
<div class="left">
|
||||
<p class="title">
|
||||
通信中断
|
||||
</p>
|
||||
<p class="text">INV-2023-003 与监控系统通信中断</p>
|
||||
</div>
|
||||
<div class="right">
|
||||
<div class="time">
|
||||
20分钟前
|
||||
</div>
|
||||
<el-text type="primary" size="small">查看详情</el-text>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item danger">
|
||||
<div class="left">
|
||||
<p class="title">
|
||||
通信中断
|
||||
</p>
|
||||
<p class="text">INV-2023-003 与监控系统通信中断</p>
|
||||
</div>
|
||||
<div class="right">
|
||||
<div class="time">
|
||||
1小时前
|
||||
</div>
|
||||
<el-text type="primary" size="small">查看详情</el-text>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item grey">
|
||||
<div class="left">
|
||||
<p class="title">
|
||||
逆变器温度过高
|
||||
</p>
|
||||
<p class="text">INV-2023-003 温度达到52℃,超过阈值48℃</p>
|
||||
</div>
|
||||
<div class="right">
|
||||
<div class="time">
|
||||
2小时前
|
||||
</div>
|
||||
<!-- <el-text type="primary" size="small">查看详情</el-text> -->
|
||||
<el-button type="info" size="small" disabled>已处理</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</template>
|
||||
<style scoped lang="scss">
|
||||
.notice {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px solid rgba(32, 32, 32, 0.05);
|
||||
padding-bottom: 20px;
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
p {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
margin-right: 10px;
|
||||
|
||||
img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.content p:nth-child(2) {
|
||||
font-size: 14px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
.item {
|
||||
border-radius: 10px;
|
||||
padding: 0 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.text {
|
||||
color: rgba(125, 133, 146, 1);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.right {
|
||||
font-size: 12px;
|
||||
color: rgba(125, 133, 146, 1);
|
||||
|
||||
.time {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.waring {
|
||||
background-color: #FFFCE6;
|
||||
border: 1px solid #FFF0E1;
|
||||
|
||||
.title {
|
||||
color: #FFA408;
|
||||
}
|
||||
}
|
||||
|
||||
.danger {
|
||||
background-color: #FFE9E5;
|
||||
border: 1px solid #FFEFEB;
|
||||
|
||||
.title {
|
||||
color: rgba(255, 87, 51, 1);
|
||||
}
|
||||
}
|
||||
|
||||
.grey {
|
||||
background-color: #F3F3F3;
|
||||
border: 1px solid #FCFCFC;
|
||||
|
||||
.right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.title {
|
||||
color: rgba(102, 102, 102, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<script setup></script>
|
||||
216
src/views/pvSystem/siteOverview/components/qixiang.vue
Normal file
@ -0,0 +1,216 @@
|
||||
<template>
|
||||
<div class="cardItem">
|
||||
<el-card>
|
||||
<div class="tianqi"
|
||||
style="display: flex; flex-direction: column; align-items: center; background-color: #fafafa; border-radius: 10px; padding-bottom: 40px">
|
||||
<div>
|
||||
<img src="/assets/Sunny.png" style="display: block; width: 100px; height: 100px" alt="" />
|
||||
</div>
|
||||
<div style="font-family: 'Alibaba-PuHuiTi-Bold'; font-size: 24px">31℃</div>
|
||||
<div>晴朗</div>
|
||||
<div style="color: rgba(154, 154, 154, 1); font-size: 14px">紫外线强度:<span>高</span></div>
|
||||
<div class="tianqi2">
|
||||
<div class="item">
|
||||
<div>
|
||||
<img src="/assets/shidu.png" alt="" />
|
||||
</div>
|
||||
<div class="text">相对湿度: <span>45%</span></div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div>
|
||||
<img src="/assets/qiangdu.png" alt="" />
|
||||
</div>
|
||||
<div class="text">光照强度: <span>45%</span></div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div>
|
||||
<img src="/assets/fengshu.png" alt="" />
|
||||
</div>
|
||||
<div class="text">风速: <span>2.3m/s</span></div>
|
||||
</div>
|
||||
<div class="item">
|
||||
<div>
|
||||
<img src="/assets/riluo.png" alt="" />
|
||||
</div>
|
||||
<div class="text">日落时间: <span>19.45</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="weather-timeline">
|
||||
<div class="time-box">
|
||||
<div class="time-item">
|
||||
<div class="time">16:00</div>
|
||||
<div class="temp">30°C</div>
|
||||
<div class="img-box">
|
||||
<img src="/assets/sunny_s.png" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="time-item">
|
||||
<div class="time">17:00</div>
|
||||
<div class="temp">29°C</div>
|
||||
<div class="img-box">
|
||||
<img src="/assets/sunny_s.png" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="time-item">
|
||||
<div class="child">
|
||||
<div class="time">18:00</div>
|
||||
<div class="temp">25°C</div>
|
||||
<div class="img-box">
|
||||
<img src="/assets/rain.png" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="time-item show">
|
||||
<div class="time">现在</div>
|
||||
<div class="temp">25°C</div>
|
||||
<div class="img-box">
|
||||
<img src="/assets/rain_show.png" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="time-item">
|
||||
<div class="time">20:00</div>
|
||||
<div class="temp">25°C</div>
|
||||
<div class="img-box">
|
||||
<img src="/assets/yin.png" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="time-item">
|
||||
<div class="time">21:00</div>
|
||||
<div class="temp">20°C</div>
|
||||
<div class="img-box">
|
||||
<img src="/assets/yin.png" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
<style scoped lang="scss">
|
||||
.cardItem {
|
||||
padding: -20px !important;
|
||||
}
|
||||
|
||||
.tianqi2 {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
margin-top: 50px;
|
||||
|
||||
img {
|
||||
width: 40px;
|
||||
height: 40x;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
|
||||
.text {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.weather-timeline {
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
margin: 15px 0;
|
||||
|
||||
.time {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.img-box {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
// img[src*='Sunny'] {
|
||||
// width: 50px;
|
||||
// height: 50px;
|
||||
// }
|
||||
|
||||
// img[src*='rain'] {
|
||||
// width: 60px;
|
||||
// height: 60px;
|
||||
// }
|
||||
|
||||
padding: 15px;
|
||||
background: linear-gradient(to right, #d6e2ff, #deebff);
|
||||
border-radius: 15px;
|
||||
|
||||
.time-box {
|
||||
background: linear-gradient(to right, #447bff, #67a3fd);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
border-radius: 10px;
|
||||
padding: 10px 20px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.time-item.show {
|
||||
color: rgba(24, 109, 245, 1);
|
||||
position: relative;
|
||||
// z-index: 888;
|
||||
background-color: #fff;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
.show::after {
|
||||
// color: rgba(24, 109, 245, 1);
|
||||
// position: relative;
|
||||
// z-index: 888;
|
||||
// background-color: #fff;
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 25px;
|
||||
// background-color: red;
|
||||
background-color: #fff;
|
||||
left: 0;
|
||||
border-radius: 0 0 25px 25px;
|
||||
}
|
||||
|
||||
.show::before {
|
||||
// color: rgba(24, 109, 245, 1);
|
||||
// position: relative;
|
||||
// z-index: 888;
|
||||
// background-color: #fff;
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 25px;
|
||||
// background-color: red;
|
||||
background-color: #fff;
|
||||
left: 0;
|
||||
top: -25px;
|
||||
border-radius: 25px 25px 0 0;
|
||||
}
|
||||
|
||||
// .show::after {
|
||||
// content: '';
|
||||
// position: absolute;
|
||||
// height: 155px;
|
||||
// background-color: #fff;
|
||||
// z-index: 999;
|
||||
// width: 100%;
|
||||
// top: -25px;
|
||||
// left: 0;
|
||||
// border-radius: 20px;
|
||||
// }
|
||||
}
|
||||
</style>
|
||||
<script setup></script>
|
||||
107
src/views/pvSystem/siteOverview/components/status.vue
Normal file
@ -0,0 +1,107 @@
|
||||
<template>
|
||||
<el-card shadow="never" style="border-radius: 10px;">
|
||||
<el-form :inline="true" :model="formInline" label-width="120" style="display: flex; justify-content: center;">
|
||||
<el-form-item label="规则编号">
|
||||
<el-input v-model="formInline.user" placeholder="请输入规则编号" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-select v-model="formInline.region" placeholder="请输入状态" clearable>
|
||||
<el-option label="Zone one" value="shanghai" />
|
||||
<el-option label="Zone two" value="beijing" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="更新时间">
|
||||
<el-date-picker v-model="formInline.date" type="date" placeholder="请选择时间" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button icon="search" type="primary" @click="onSubmit">搜索</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button icon="refresh" type="default" @click="onSubmit">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-table v-loading="loading" :data="listData" @selection-change="handleSelectionChange">
|
||||
<!-- <el-table-column type="index" width="50" label="序号" /> -->
|
||||
<el-table-column label="逆变器编号" align="center" prop="id" />
|
||||
<!-- <el-table-column label="合同类型" align="center" prop="contractType">
|
||||
<template #default="scope">
|
||||
<dict-tag :options="income_contract_type" :value="scope.row.contractType" />
|
||||
</template>
|
||||
</el-table-column> -->
|
||||
<el-table-column label="输出功率" align="center" prop="shuchu" />
|
||||
<el-table-column label="效率" align="center" prop="xiaolv" />
|
||||
<el-table-column label="温度" align="center" prop="wendu" />
|
||||
<el-table-column label="今日发电量" align="center" prop="fadian" />
|
||||
<el-table-column label="状态" align="center" prop="status">
|
||||
<template #default="scope">
|
||||
<!-- <el-button link type="primary">详情</el-button>
|
||||
<el-button link type="danger">处理</el-button>
|
||||
<el-button link type="warning">维护记录</el-button> -->
|
||||
<el-tag :type="statusMap[scope.row.status].type">{{ statusMap[scope.row.status].label }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="200">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary">详情</el-button>
|
||||
<el-button link type="danger">处理</el-button>
|
||||
<el-button link type="warning">维护记录</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize" @pagination="getList" /> -->
|
||||
<pagination :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList" />
|
||||
</el-card>
|
||||
</template>
|
||||
<script setup>
|
||||
const formInline = ref({})
|
||||
const total = ref(0);
|
||||
const loading = ref(false);
|
||||
const listData = [
|
||||
{ id: "INV-2023-001", shuchu: "12.8kw", xiaolv: "98.2%", wendu: "42℃", fadian: "158.5kWh", status: 1 },
|
||||
{ id: "INV-2023-001", shuchu: "12.8kw", xiaolv: "98.2%", wendu: "42℃", fadian: "158.5kWh", status: 1 },
|
||||
{ id: "INV-2023-001", shuchu: "12.8kw", xiaolv: "98.2%", wendu: "42℃", fadian: "158.5kWh", status: 2 },
|
||||
{ id: "INV-2023-001", shuchu: "12.8kw", xiaolv: "98.2%", wendu: "42℃", fadian: "158.5kWh", status: 2 },
|
||||
{ id: "INV-2023-001", shuchu: "12.8kw", xiaolv: "98.2%", wendu: "42℃", fadian: "158.5kWh", status: 3 },
|
||||
{ id: "INV-2023-001", shuchu: "12.8kw", xiaolv: "98.2%", wendu: "42℃", fadian: "158.5kWh", status: 3 }
|
||||
]
|
||||
const statusMap = {
|
||||
1: {
|
||||
label: "正常运行",
|
||||
type: "success"
|
||||
},
|
||||
2: {
|
||||
label: "异常",
|
||||
type: "danger"
|
||||
},
|
||||
3: {
|
||||
label: "维护中",
|
||||
type: "warning"
|
||||
}
|
||||
}
|
||||
const initFormData = {
|
||||
|
||||
};
|
||||
const data = reactive({
|
||||
form: { ...initFormData },
|
||||
queryParams: {
|
||||
|
||||
},
|
||||
});
|
||||
const { queryParams, form, rules } = toRefs(data);
|
||||
|
||||
/** 多选框选中数据 */
|
||||
const handleSelectionChange = (selection) => {
|
||||
ids.value = selection.map((item) => item.id);
|
||||
single.value = selection.length != 1;
|
||||
multiple.value = !selection.length;
|
||||
};
|
||||
const getList = async () => {
|
||||
// loading.value = true;
|
||||
// const res = await listIncomeContract(queryParams.value);
|
||||
// incomeContractList.value = res.rows;
|
||||
// total.value = res.total;
|
||||
// loading.value = false;
|
||||
};
|
||||
</script>
|
||||