合并develop最新代码

This commit is contained in:
2025-08-20 17:53:26 +08:00
45 changed files with 3485 additions and 1325 deletions

View File

@ -7,6 +7,13 @@ export function getDzjkHomeView(siteId) {
method: 'get' method: 'get'
}) })
} }
//站点首页 冲放曲线
export function getSevenChargeData({siteId,startDate,endDate}) {
return request({
url: `/ems/siteMap/getSevenChargeData?siteId=${siteId}&startDate=${startDate}&endDate=${endDate}`,
method: 'get'
})
}
//获取pcs、实时运行头部的设备信息 //获取pcs、实时运行头部的设备信息
export function getRunningHeadInfo(siteId) { export function getRunningHeadInfo(siteId) {
@ -47,16 +54,16 @@ export function getStackNameList(siteId) {
}) })
} }
//获取单体电池 电池簇列表数据 //获取单体电池 电池簇列表数据
export function getClusterNameList(stackDeviceId) { export function getClusterNameList({stackDeviceId,siteId}) {
return request({ return request({
url: `/ems/siteMonitor/getClusterNameList?stackDeviceId=${stackDeviceId}`, url: `/ems/siteMonitor/getClusterNameList?stackDeviceId=${stackDeviceId}&siteId=${siteId}`,
method: 'get' method: 'get'
}) })
} }
//单体电池表格数据 //单体电池表格数据
export function getClusterDataInfoList({siteId, stackDeviceId, clusterDeviceId, pageSize, pageNum}) { export function getClusterDataInfoList({siteId, stackDeviceId, clusterDeviceId,batteryId, pageSize, pageNum}) {
return request({ return request({
url: `/ems/siteMonitor/getClusterDataInfoList?clusterDeviceId=${clusterDeviceId}&siteId=${siteId}&stackDeviceId=${stackDeviceId}&pageSize=${pageSize}&pageNum=${pageNum}`, url: `/ems/siteMonitor/getClusterDataInfoList?clusterDeviceId=${clusterDeviceId}&siteId=${siteId}&stackDeviceId=${stackDeviceId}&batteryId=${batteryId}&pageSize=${pageSize}&pageNum=${pageNum}`,
method: 'get' method: 'get'
}) })
} }
@ -86,9 +93,9 @@ export function getAmmeterDataList(siteId) {
} }
// 故障告警 // 故障告警
export function getAlarmDetailList({siteId, deviceType, alarmLevel, alarmStartTime, alarmEndTime,pageSize,pageNum}) { export function getAlarmDetailList({siteId, deviceId, alarmLevel, alarmStartTime, alarmEndTime,pageSize,pageNum}) {
return request({ return request({
url: `/ems/siteAlarm/getAlarmDetailList?siteId=${siteId}&deviceType=${deviceType}&alarmLevel=${alarmLevel}&alarmStartTime=${alarmStartTime}&alarmEndTime=${alarmEndTime}&pageSize=${pageSize}&pageNum=${pageNum}`, url: `/ems/siteAlarm/getAlarmDetailList?siteId=${siteId}&deviceId=${deviceId}&alarmLevel=${alarmLevel}&alarmStartTime=${alarmStartTime}&alarmEndTime=${alarmEndTime}&pageSize=${pageSize}&pageNum=${pageNum}`,
method: 'get' method: 'get'
}) })
} }
@ -277,10 +284,10 @@ export function setTimeConfigList(data) {
}) })
} }
// 策略曲线图 // 策略曲线图
//http://localhost:8089/strategy/curve/list?strategyId=1&siteId=021_FXX_01 //http://localhost:8089/strategy/curve/curveList?strategyId=1&siteId=021_FXX_01
export function curveList({siteId,strategyId}) { export function curveList({siteId,strategyId}) {
return request({ return request({
url: `/strategy/curve/list?siteId=${siteId}&strategyId=${strategyId}`, url: `/strategy/curve/curveList?siteId=${siteId}&strategyId=${strategyId}`,
method: 'get', method: 'get',
}) })
} }

View File

@ -126,28 +126,37 @@
background: #0366c1!important; background: #0366c1!important;
} }
} }
.ems-third-menu{ .ems-third-menu-container{
border-right: none; position: relative;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); padding-left: 160px;
height: fit-content; background-color: #ffffff;
.el-menu-item{ .ems-third-menu{
line-height: 45px; border-right: none;
height: 45px; box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
padding: 0 !important; height: fit-content;
width: 125px; position: absolute;
text-align: center; top:20px;
} left:20px;
.el-menu-item:hover{ .el-menu-item{
background: #67b1ff!important; line-height: 45px;
color:#ffffff!important; height: 45px;
} padding: 0 !important;
.el-menu-item.is-active{ width: 125px;
background: #409eff!important; text-align: center;
} }
.el-menu-item:hover{
background: #67b1ff!important;
color:#ffffff!important;
}
.el-menu-item.is-active{
background: #409eff!important;
}
}
} }
//按钮栏 选中样式 //按钮栏 选中样式
.ems-btns-group{ .ems-btns-group{
.activeBtn{ .activeBtn{

View File

@ -0,0 +1,98 @@
<template>
<div class="time-range">
<el-date-picker
v-model="dateRange"
type="daterange"
range-separator=""
start-placeholder="开始时间"
value-format="yyyy-MM-dd"
:clearable="false"
:picker-options="pickerOptions"
end-placeholder="结束时间">
</el-date-picker>
<el-button size="mini" style="margin-left: 10px;" :loading="loading" @click="reset">重置</el-button>
<el-button type="primary" size="mini" :loading="loading" @click="search">搜索</el-button>
<el-button type="primary" size="mini" :loading="loading" @click="timeLine('before')">上一时段</el-button>
<el-button type="primary" size="mini" :loading="loading" @click="timeLine('next')" :disabled="disabledNextBtn">下一时段</el-button>
</div>
</template>
<script>
import {formatDate} from '@/filters/ems'
export default {
computed:{
disabledNextBtn(){
return new Date(this.dateRange[1]) >= new Date(this.defaultDateRange[1])
}
},
data() {
return {
loading:false,
dateRange:[],
defaultDateRange:[],
pickerOptions:{
disabledDate(time) {
return time.getTime() > Date.now();
},
},
}
},
methods: {
init(){
const now = new Date(),formatNow = formatDate(now);
const weekAgo = formatDate(new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000))
this.dateRange = [weekAgo, formatNow];
this.defaultDateRange=[weekAgo, formatNow];
this.$emit('updateDate',this.dateRange)
},
showBtnLoading(status){
this.loading = status
},
resetDate(){
this.dateRange = this.defaultDateRange
},
//重置 设置时间范围为初始化时间段
reset(){
this.resetDate()
this.$emit('updateDate',this.dateRange)
},
// 搜索
search(){
this.$emit('updateDate',this.dateRange)
},
timeLine(type){
//baseTime,maxTime 毫秒数
let baseTime = type === 'before' ? new Date(this.dateRange[0]).getTime() - ( 24 * 60 * 60 * 1000) :new Date(this.dateRange[1]).getTime() + ( 24 * 60 * 60 * 1000) ,
maxTime = new Date(this.defaultDateRange[1]).getTime()
//updateTime 毫秒数
let updateTime = type === 'before' ? baseTime - 7 * 24 * 60 * 60 * 1000 : baseTime + 7 * 24 * 60 * 60 * 1000
if(type === 'next' && updateTime >= maxTime) updateTime = maxTime
const start = formatDate(type === 'before' ? updateTime : baseTime)
const end = formatDate(type === 'before' ? baseTime : updateTime)
this.dateRange = [start,end]
this.$emit('updateDate',this.dateRange)
},
}
}
</script>
<style lang="scss" scoped>
.time-range{
display: flex;
::v-deep {
.el-range-editor--medium .el-range__icon, .el-range-editor--medium .el-range__close-icon{
line-height: 22px;
}
.el-range-editor--medium.el-input__inner{
height: 30px;
}
.el-range-editor--medium .el-range-separator{
line-height: 24px;
}
.el-button--mini{
padding:3px 10px;
}
}
}
</style>

View File

@ -1,23 +1,5 @@
import Layout from "@/layout/index.vue"; import Layout from "@/layout/index.vue";
//todo delete 删除动态路由页面的定义 接口会传递进来
// const ems = [
// {
// path: '',
// component: Layout,
// redirect: 'noRedirect',
// children: [
// {
// path: 'zddt',
// component: () => import('@/views/ems/zddt/index'),
// name: 'zddt',
// meta: { title: '站点地图', icon: 'guide' }
// }
// ]
// }
// ]
// export default ems
//单站监控 //单站监控
// todo 本地设置了 hidden:true,不会显示在侧边栏,需要在系统管理、菜单管理中手动添加菜单后才会展示在侧边栏 // todo 本地设置了 hidden:true,不会显示在侧边栏,需要在系统管理、菜单管理中手动添加菜单后才会展示在侧边栏
export const dzjk=[ export const dzjk=[

View File

@ -1,7 +1,5 @@
import Vue from 'vue' import Vue from 'vue'
import Router from 'vue-router' import Router from 'vue-router'
// todo delete
import ems from './ems'//EMS管理系统routers引用
import {dzjk} from '@/router/ems' import {dzjk} from '@/router/ems'
Vue.use(Router) Vue.use(Router)
@ -92,7 +90,6 @@ export const constantRoutes = [
] ]
}, },
// EMS管理系统routers // EMS管理系统routers
// ...ems
...dzjk ...dzjk
] ]

View File

@ -47,11 +47,34 @@ export default {
}, },
setOption(data) { setOption(data) {
if(!this.chart) return if(!this.chart) return
let obj = {}
for(var i=0;i<=23;i++){
obj[i] = {
title:i<=9?`0${i}:00` : `${i}:00`
}
}
const nowMonth = new Date().getMonth()+1;
const localMonth = data.find(item=>item.month === nowMonth)?.powerList || []
localMonth.forEach(item => {
const startHours = parseInt(item.startTime.split(':')[0], 10)
const endHours =parseInt(item.endTime.split(':')[0], 10)
for(let i=startHours;i<=endHours;i++){
obj[i].value = item.powerData
}
})
let source = [['时间','冲放功率']]
Object.values(obj).forEach(item => {
const {title,value} = item
source.push([title,value])
})
this.chart.setOption({ this.chart.setOption({
color:['#FFBD00','#3C81FF'], color:['#FFBD00','#3C81FF'],
grid: {
containLabel: true
},
legend: { legend: {
left: 'center', left: 'center',
bottom: '10', bottom: '15',
}, },
tooltip: { tooltip: {
trigger: 'axis', trigger: 'axis',
@ -63,7 +86,7 @@ export default {
color:"#333333", color:"#333333",
}, },
xAxis: { xAxis: {
data: ['01:00','02:00','03:00','05:00','06:00','07:00','08:00','09:00','10:00'], type: 'category',
axisLine: { axisLine: {
lineStyle:{ lineStyle:{
color: '#333333', color: '#333333',
@ -78,16 +101,14 @@ export default {
} }
} }
}, },
dataset: {
source
},
series: [ series: [
{ {
name:'模板一', name:'冲放功率',
data: [80,92,1,34,90,130,320,80,9,91],
type: 'line', type: 'line',
},{ }]
name:'模板二',
data: [820,932,901,934,1290,1330,1320,820,932,901],
type: 'line',
}]
}) })
} }
} }

View File

@ -40,7 +40,6 @@ export default {
this.$refs.tomePowerChart.changeSiteId() this.$refs.tomePowerChart.changeSiteId()
}) })
}, },
//在编辑、删除模板后更新时间配置、echart的数据todo
updateTimeSetting(){ updateTimeSetting(){
this.$refs.timeSetting.init() this.$refs.timeSetting.init()
this.$refs.tomePowerChart.init() this.$refs.tomePowerChart.init()

View File

@ -3,9 +3,9 @@
<div v-loading="loading" class="ems-dashboard-editor-container ems-content-container-padding"> <div v-loading="loading" class="ems-dashboard-editor-container ems-content-container-padding">
<!-- 搜索栏--> <!-- 搜索栏-->
<el-form :inline="true" class="select-container"> <el-form :inline="true" class="select-container">
<el-form-item label="设备类型"> <el-form-item label="设备清单">
<el-select v-model="search.deviceType" clearable placeholder="请选择" :loading="loading" loading-text="正在加载数据"> <el-select v-model="search.deviceId" clearable placeholder="请选择" :loading="loading" loading-text="正在加载数据">
<el-option :label="value" :value="key" v-for="(value,key) in $store.state.ems.deviceTypeOptions" :key="key+'deviceTypeOptions'"></el-option> <el-option :label="item.deviceName" :value="item.deviceId" v-for="(item,key) in deviceOptions" :key="key+'deviceIdOptions'"></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="告警等级"> <el-form-item label="告警等级">
@ -114,6 +114,7 @@
<script> <script>
import {getAlarmDetailList} from'@/api/ems/dzjk' import {getAlarmDetailList} from'@/api/ems/dzjk'
import {getDeviceList} from'@/api/ems/site'
import getQuerySiteId from "@/mixins/ems/getQuerySiteId"; import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
import { formatDate } from '@/filters/ems' import { formatDate } from '@/filters/ems'
export default { export default {
@ -126,6 +127,7 @@ export default {
{name:'今日告警',id:'today'}, {name:'今日告警',id:'today'},
{name:'历史告警',id:'history'}, {name:'历史告警',id:'history'},
], ],
deviceOptions:[],//设备列表
pickerOptions:{ pickerOptions:{
disabledDate(time) { disabledDate(time) {
return time.getTime() > Date.now(); return time.getTime() > Date.now();
@ -134,7 +136,7 @@ export default {
defaultDateRange:[],//默认展示的时间 defaultDateRange:[],//默认展示的时间
dateRange:[],//alarmStartTime,alarmEndTime dateRange:[],//alarmStartTime,alarmEndTime
activeBtn:'today', activeBtn:'today',
search:{deviceType:'',alarmLevel:''}, search:{deviceId:'',alarmLevel:''},
// 表格、分页 // 表格、分页
tableData:[], tableData:[],
pageSize:10,//分页栏当前每个数据总数 pageSize:10,//分页栏当前每个数据总数
@ -186,7 +188,7 @@ export default {
}, },
// 重置 // 重置
onReset(){ onReset(){
this.search={deviceType:'',alarmLevel:''} this.search={deviceId:'',alarmLevel:''}
this.dateRange=[] this.dateRange=[]
this.pageNum =1//每次搜索从1开始搜索 this.pageNum =1//每次搜索从1开始搜索
this.getData() this.getData()
@ -213,7 +215,7 @@ export default {
// 获取数据 // 获取数据
getData(){ getData(){
this.loading=true this.loading=true
const {deviceType,alarmLevel} = this.search const {deviceId,alarmLevel} = this.search
const {siteId,pageNum,pageSize,activeBtn} =this const {siteId,pageNum,pageSize,activeBtn} =this
const [alarmStartTime='',alarmEndTime='']=(this.dateRange || []) const [alarmStartTime='',alarmEndTime='']=(this.dateRange || [])
let start='',end = '',now =new Date() let start='',end = '',now =new Date()
@ -230,12 +232,18 @@ export default {
// end.setDate(end.getDate() - 1); // end.setDate(end.getDate() - 1);
} }
} }
getAlarmDetailList({deviceType,alarmLevel,siteId,pageSize,pageNum,alarmStartTime:formatDate(start),alarmEndTime:formatDate(end)}).then(response => { getAlarmDetailList({deviceId,alarmLevel,siteId,pageSize,pageNum,alarmStartTime:formatDate(start),alarmEndTime:formatDate(end)}).then(response => {
this.tableData=response?.rows || []; this.tableData=response?.rows || [];
this.totalSize = response?.total || 0 this.totalSize = response?.total || 0
}).finally(() => {this.loading=false}) }).finally(() => {this.loading=false})
}, },
getDeviceOptions(){
getDeviceList(this.siteId).then(response => {
this.deviceOptions = JSON.parse(JSON.stringify(response?.data || []))
})
},
init(){ init(){
this.getDeviceOptions()
this.onReset() this.onReset()
}, },
}, },

View File

@ -0,0 +1,125 @@
<template>
<el-card shadow="always" class="common-card-container common-card-container-body-no-padding time-range-card">
<div slot="header" class="time-range-header">
<span class="card-title">当日功率曲线</span>
<date-range-select ref="dateRangeSelect" @updateDate="updateDate"/>
</div>
<div style="height: 310px" id="activeChart"></div>
</el-card>
</template>
<script>
import * as echarts from 'echarts'
import resize from '@/mixins/ems/resize'
import DateRangeSelect from '@/components/Ems/DateRangeSelect/index.vue'
import {getPcsNameList, getPowerData} from '@/api/ems/dzjk'
export default {
mixins: [resize],
components: {DateRangeSelect},
data() {
return {
chart: null,
timeRange:[],
siteId:'',
deviceId:''
}
},
mounted() {
this.$nextTick(() => {
this.initChart()
})
},
beforeDestroy() {
if (!this.chart) {
return
}
this.chart.dispose()
this.chart = null
},
methods: {
// 更新时间范围 重置图表
updateDate(data){
this.timeRange=data
this.getGVQXData()
},
getGVQXData(){
this.showLoading()
const {siteId,deviceId,timeRange}=this
if(!deviceId) return this.hideLoading()
getPowerData({siteId,deviceId,startDate:timeRange[0],endDate:timeRange[1],dataType:'1'}).then(response => {
this.setOption(response?.data || [])
}).finally(()=>this.hideLoading())
},
init(siteId){
//初始化 清空数据
this.siteId = siteId
this.timeRange=[]
this.deviceId=''
this.$refs.dateRangeSelect.init()
this.showLoading()
getPcsNameList(siteId).then(response=>{
const data=response?.data || [];
if(data.length>0){
this.deviceId=data[0].id
//接口调用完成之后 设置图表、结束loading
this.getGVQXData()
}else{
this.hideLoading()
}
})
},
initChart() {
this.chart = echarts.init(document.querySelector('#activeChart'))
},
showLoading(){
this.chart && this.chart.showLoading()
},
hideLoading(){
this.chart && this.chart.hideLoading()
},
setOption(data) {
const source = [['日期','电网功率']]
this.chart && data.forEach((item)=>{
source.push([item.statisDate,item.gridPower])
})
this.chart.setOption({
color:['#FFBD00','#3C81FF'],
grid: {
containLabel: true
},
legend: {
left: 'center',
bottom: '15',
},
tooltip: {
trigger: 'axis',
axisPointer: { // 坐标轴指示器,坐标轴触发有效
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
}
},
textStyle:{
color:"#333333",
},
xAxis: {
type: 'category',
},
yAxis: {
type: 'value',
},
dataset:{source},
series: [
{
name:'电网功率',
type: 'line',
}
]
})
},
}
}
</script>

View File

@ -0,0 +1,86 @@
<template>
<!-- <el-card shadow="always" class="common-card-container common-card-container-body-no-padding">
<div slot="header">
<span class="card-title">当前报警</span>
</div>
<div class="ssgj-container">
<el-table
class="common-table"
:data="tableData"
height="100%"
stripe
style="width: 100%">
<el-table-column
prop="deviceName"
label="名称">
</el-table-column>
<el-table-column
label="状态"
>
<template slot-scope="scope">
<span :class="{'circle warning-status' : scope.row.status !== 0}">{{ $store.state.ems.warnOptions[scope.row.status]}}</span>
</template>
</el-table-column>
<el-table-column
class-name="alarm-content"
prop="alarmContent"
show-overflow-tooltip
label="告警内容">
</el-table-column>
<el-table-column
label="工单"
fixed="right"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-button type="text" size="mini" v-if="scope.row.ticketNo" @click="toTicket">已生成工单(工单号:{{scope.row.ticketNo}})</el-button>
<el-button type="primary" size="mini" v-else @click="toTicket">生成工单</el-button>
</template>
</el-table-column>
</el-table>
</div>
</el-card> -->
<el-alert type="error" :closable="false">
<template>
<div style="cursor: pointer" @click="toAlarm">设备告警</div>
</template>
</el-alert>
</template>
<script>
export default {
props: {
tableData: {
require: true,
type: Array,
default: () => {
return [];
},
},
},
data() {
return {};
},
methods: {
toAlarm() {
this.$router.push({ path: "/dzjk/gzgj", query: this.$route.query });
},
toTicket() {
this.$router.push({ path: "/ticket" });
},
},
};
</script>
<style lang="scss" scoped>
//实时告警
.ssgj-container {
padding: 20px;
height: 250px;
box-sizing: border-box;
::v-deep {
.el-table .el-table__header-wrapper th,
.el-table .el-table__fixed-header-wrapper th {
background: #fff2cb;
}
}
}
</style>

View File

@ -0,0 +1,82 @@
<template>
<el-card
shadow="always"
class="common-card-container common-card-container-body-no-padding"
>
<div slot="header">
<span class="card-title">策略信息</span>
</div>
<!-- <el-empty :image-size="100" ></el-empty> -->
<div
style="
box-sizing: border-box;
height: 250px;
padding: 20px 15px;
overflow-y: auto;
"
>
<el-descriptions class="home-normal-info" :column="2">
<el-descriptions-item size="mini" label="模板名称">{{
info.mainStrategyName || "-"
}}</el-descriptions-item>
<el-descriptions-item size="mini" label="SOC限制">{{
mainInfo.sdcLimit === 1 ? "开" : mainInfo.sdcLimit === 0 ? "关" : "-"
}}</el-descriptions-item>
<el-descriptions-item size="mini" label="SOC下限%">{{
formatNumber(mainInfo.sdcDown)
}}</el-descriptions-item>
<el-descriptions-item size="mini" label="SOC上限%">{{
formatNumber(mainInfo.sdcUp)
}}</el-descriptions-item>
</el-descriptions>
<el-table
:data="info.siteMonitorDataVo || []"
border
size="mini"
style="width: 100%; margin-top: 15px"
>
<el-table-column prop="startTime" label="开始时间"> </el-table-column>
<el-table-column prop="endTime" label="结束时间"> </el-table-column>
<el-table-column prop="chargeDischargePower" label="充放功率kW">
</el-table-column>
<el-table-column prop="chargeStatus" label="充电状态">
<template slot-scope="scope">
{{ chargeStatusOptions[scope.row.chargeStatus] }}
</template>
</el-table-column>
</el-table>
</div>
</el-card>
</template>
<script>
import { mapState } from "vuex";
import { formatNumber } from "@/filters/ems";
export default {
props: {
info: {
require: true,
type: Object,
default: () => {
return {};
},
},
},
computed: {
...mapState({
chargeStatusOptions: (state) => state?.ems?.chargeStatusOptions || {},
}),
mainInfo() {
return this.info?.siteMonitorDataVo?.length > 0
? this.info.siteMonitorDataVo[0]
: {};
},
},
data() {
return {};
},
methods: {
formatNumber,
},
};
</script>
<style lang="scss" scoped></style>

View File

@ -1,148 +0,0 @@
<template>
<el-row style="background:#fff;margin-top:30px;">
<el-col :xs="24" :sm="24" :lg="24">
<el-card shadow="always" class="common-card-container common-card-container-body-no-padding">
<div slot="header">
<span class="card-title">能量流转</span>
</div>
<div style="height: 310px" id="nllzChart"></div>
</el-card>
</el-col>
</el-row>
</template>
<script>
import * as echarts from 'echarts'
import resize from '@/mixins/ems/resize'
import {formatNumber} from "@/filters/ems";
export default {
mixins: [resize],
data() {
return {
chart: null
}
},
mounted() {
this.$nextTick(() => {
this.initChart()
})
},
beforeDestroy() {
if (!this.chart) {
return
}
this.chart.dispose()
this.chart = null
},
methods: {
initChart() {
this.chart = echarts.init(document.querySelector('#nllzChart'))
},
setOption(data) {
const {siteMonitorDataVo=[],gridNrtPower,loadNrtPower,energyStorageNrtPower,energyStorageAvailElec} = data
const source = [['日期','充电量','放电量']]
siteMonitorDataVo.forEach(item=>{
source.push([item.ammeterDate, item.chargedCap,item.disChargedCap])
})
this.chart.setOption({
title: [
// 右上角
{
text: `电网 实时功率:${formatNumber(gridNrtPower)} kW`,
top: 10,
right: 10,
textStyle:{
fontSize:12,
color:'#666666'
}
},
// 右下角
{
text: `负载 实时功率:${formatNumber(loadNrtPower)} kW`,
bottom: 10,
right: 10,
textStyle:{
fontSize:12,
color:'#666666'
}
},
// 左下角第一行
{
text: '储能',
bottom: 40,
left: 10,
textStyle:{
fontSize:12,
color:'#666666'
}
},
// 左下角第二行
{
text: `实时功率:${formatNumber(energyStorageNrtPower)} kW`,
bottom: 26,
left: 10,
textStyle:{
fontSize:12,
color:'#666666'
}
},
// 左下角第三行
{
text: `可用电量:${formatNumber(energyStorageAvailElec)} kWh`,
bottom: 10,
left: 10,
textStyle:{
fontSize:12,
color:'#666666'
}
}
],
grid:{
left:200
},
tooltip: {
trigger: 'axis',
axisPointer: { // 坐标轴指示器,坐标轴触发有效
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
}
},
textStyle:{
color:"#333333",
},
xAxis: {
type: 'category',
axisLine: {
lineStyle:{
color: '#333333',
}
}
},
yAxis: {
type: 'value',
axisLine: {
lineStyle:{
color: '#333333',
}
}
},
dataset:{
source
// source: [['日期','充电量','放电量']]
},
series: [
{
name:'充电量',
type: 'line',
},{
name:'放电量',
type: 'line',
}]
})
}
}
}
</script>

View File

@ -0,0 +1,121 @@
<template>
<el-card shadow="always" class="common-card-container common-card-container-body-no-padding time-range-card">
<div slot="header" class="time-range-header">
<span class="card-title">一周充放曲线</span>
<date-range-select ref="dateRangeSelect" @updateDate="updateDate"/>
</div>
<div style="height: 310px" id="weekChart"></div>
</el-card>
</template>
<script>
import * as echarts from 'echarts'
import resize from '@/mixins/ems/resize'
import DateRangeSelect from '@/components/Ems/DateRangeSelect/index.vue'
import {getSevenChargeData} from '@/api/ems/dzjk'
export default {
mixins: [resize],
components: {DateRangeSelect},
data() {
return {
chart: null,
timeRange:[],
siteId:'',
}
},
mounted() {
this.$nextTick(() => {
this.initChart()
})
},
beforeDestroy() {
if (!this.chart) {
return
}
this.chart.dispose()
this.chart = null
},
methods: {
// 更新时间范围 重置图表
updateDate(data){
this.timeRange=data
this.getWeekKData()
},
getWeekKData(){
this.showLoading()
const {siteId,timeRange}=this
this.hideLoading()
getSevenChargeData({siteId,startDate:timeRange[0],endDate:timeRange[1]}).then(response => {
this.setOption(response?.data || [])
}).finally(()=>this.hideLoading())
},
init(siteId){
//初始化 清空数据
this.siteId = siteId
this.timeRange=[]
this.deviceId=''
this.$refs.dateRangeSelect.init()
},
initChart() {
this.chart = echarts.init(document.querySelector('#weekChart'))
},
showLoading(){
this.chart && this.chart.showLoading()
},
hideLoading(){
this.chart && this.chart.hideLoading()
},
setOption(data,unit) {
const source = [['日期','充电量','放电量']]
data.forEach(item=>{
source.push([item.ammeterDate, item.chargedCap,item.disChargedCap])
})
this.chart && this.chart.setOption({
color:['#FFBD00','#3C81FF','#05AEA3'],
tooltip: {
trigger: 'axis',
axisPointer: { // 坐标轴指示器,坐标轴触发有效
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
}
},
grid: {
containLabel: true
},
legend: {
left: 'center',
bottom: '15',
},
xAxis: {
type: 'category',
name:unit,
nameLocation:'center'
},
yAxis: [{
type: 'value',
name:'充电量/放电量kWh',
axisLine: {
lineStyle:{
color: '#333333',
},
onZero:false
}
}],
dataset:{
source
},
series: [
{
yAxisIndex:0,
type: 'line',
},
{
yAxisIndex:0,
type: 'line',
},
]
})
}
}
}
</script>

View File

@ -1,148 +1,221 @@
<template> <template>
<div v-loading="loading" class="ems-dashboard-editor-container"> <div
<el-row :gutter="32" style="background:#fff;"> class="ems-dashboard-editor-container ems-content-container-padding"
<el-col :xs="24" :sm="24" :lg="10"> v-loading="loading"
<el-card shadow="always" class="common-card-container common-card-container-body-no-padding"> >
<el-row style="background: #fff" class="row-container" :gutter="15">
<el-col v-if="tableData.length > 0" :xs="24" :sm="24" :lg="24">
<alarm-table :tableData="tableData" />
</el-col>
<el-col :xs="24" :sm="24" :lg="6">
<el-card
shadow="always"
class="common-card-container common-card-container-body-no-padding"
>
<div slot="header"> <div slot="header">
<span class="card-title">数据概览</span> <span class="card-title">静态信息</span>
</div> </div>
<div style="height: 310px" > <div
style="box-sizing: border-box; height: 250px; padding: 20px 15px"
>
<el-descriptions class="home-normal-info" :column="1">
<el-descriptions-item
size="mini"
v-for="(item, index) in singleZdInfo"
:key="index + 'singleZdInfo'"
:label="item.title"
>{{ info[item.attr] | formatNumber }}</el-descriptions-item
>
</el-descriptions>
</div>
</el-card>
</el-col>
<el-col :xs="24" :sm="24" :lg="8">
<el-card
shadow="always"
class="common-card-container common-card-container-body-no-padding"
>
<div slot="header">
<span class="card-title">总累计运行数据</span>
</div>
<div
style="box-sizing: border-box; height: 250px; padding: 20px 15px"
>
<el-row :gutter="20"> <el-row :gutter="20">
<el-col :span="12" v-for="(item,index) in sjglData" :key="index+'sjglData'" class="sjgl-data"> <el-col
<div class="sjgl-title">{{item.title}}</div> :span="12"
<div class="sjgl-value">{{item.value | formatNumber}}</div> v-for="(item, index) in sjglData"
:key="index + 'sjglData'"
class="sjgl-data"
>
<div class="sjgl-title">{{ item.title }}</div>
<div class="sjgl-value">
{{ runningInfo[item.attr] | formatNumber }}
</div>
</el-col> </el-col>
</el-row> </el-row>
</div> </div>
</el-card> </el-card>
</el-col> </el-col>
<el-col :xs="24" :sm="24" :lg="14"> <el-col :xs="24" :sm="24" :lg="10">
<el-card shadow="always" class="common-card-container common-card-container-body-no-padding"> <cl-info :info="runningInfo.strategyTempInfo" />
<div slot="header"> </el-col>
<span class="card-title">实时告警</span> <el-col :xs="24" :sm="24" :lg="24">
<!-- <el-button style="float: right; padding: 3px 0" type="text" size="small">通讯状态<span style="color:red">超时</span></el-button>--> <week-chart ref="weekChart" />
</div> </el-col>
<div class="ssgj-container"> <el-col :xs="24" :sm="24" :lg="24">
<el-table <active-chart ref="activeChart" />
class="common-table"
:data="tableData"
height="100%"
stripe
style="width: 100%">
<el-table-column
prop="deviceName"
label="名称">
</el-table-column>
<el-table-column
label="状态"
>
<template slot-scope="scope">
<span :class="{'circle warning-status' : scope.row.status !== 0}">{{ $store.state.ems.warnOptions[scope.row.status]}}</span>
</template>
</el-table-column>
<el-table-column
class-name="alarm-content"
prop="alarmContent"
label="告警内容">
</el-table-column>
<el-table-column
label="工单"
fixed="right"
width="250"
>
<template slot-scope="scope">
<el-button type="text" size="mini" v-if="scope.row.ticketNo" @click="toTicket">已生成工单(工单号:{{scope.row.ticketNo}})</el-button>
<el-button type="primary" size="mini" v-else @click="toTicket">生成工单</el-button>
</template>
</el-table-column>
</el-table>
</div>
</el-card>
</el-col> </el-col>
</el-row> </el-row>
<nllz-chart ref="nllzChart"/>
</div> </div>
</template> </template>
<script> <script>
import {getDzjkHomeView} from '@/api/ems/dzjk' import { getSingleSiteBaseInfo } from "@/api/ems/zddt";
import NllzChart from "./NllzChart.vue"; import { getDzjkHomeView } from "@/api/ems/dzjk";
import getQuerySiteId from '@/mixins/ems/getQuerySiteId' import WeekChart from "./WeekChart.vue";
import ActiveChart from "./ActiveChart.vue";
import AlarmTable from "./AlarmTable.vue";
import ClInfo from "./ClInfo.vue";
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
export default { export default {
name:'DzjkSbjkHome', name: "DzjkSbjkHome",
components: {NllzChart}, components: { WeekChart, ActiveChart, AlarmTable, ClInfo },
mixins: [getQuerySiteId], mixins: [getQuerySiteId],
data() { data() {
return { return {
loading:false, loading: false,
sjglData:[{ singleZdInfo: [
title:'今日充电量MWh', {
value:'', title: "电站位置",
attr:'dayChargedCap' attr: "siteAddress",
},{ },
title:'今日放电量MWh', {
value:'', title: "投运时间",
attr:'dayDisChargedCap' attr: "runningTime",
},{ },
title:'总充电量MWh', {
value:'', title: "装机功率(MW)",
attr:'totalChargedCap' attr: "installPower",
},{ },
title:'总放电量MWh', {
value:'', title: "装机容量(MW)",
attr:'totalDischargedCap' attr: "installCapacity",
}], },
// todo 表格里的不同状态可能需要显示不同颜色 确定表格内容 ],
tableData:[] sjglData: [
} {
title: "今日充电量MWh",
attr: "dayChargedCap",
},
{
title: "今日放电量MWh",
attr: "dayDisChargedCap",
},
{
title: "总充电量MWh",
attr: "totalChargedCap",
},
{
title: "总放电量MWh",
attr: "totalDischargedCap",
},
],
info: {}, //基本信息
runningInfo: {}, //总累计运行数据+报警表格
};
}, },
methods:{ computed: {
toTicket(){ tableData() {
this.$router.push({path:'/ticket'}) console.log(
"this.runningInfo?.siteMonitorHomeAlarmVo ",
this.runningInfo?.siteMonitorHomeAlarmVo
);
return this.runningInfo?.siteMonitorHomeAlarmVo || [];
}, },
init(){
this.loading = true
getDzjkHomeView(this.siteId).then(response => {
const data = response?.data || {}
this.sjglData.forEach(item=>{
item.value =data[item.attr]
})
this.tableData = data?.siteMonitorHomeAlarmVo || []
this.$refs.nllzChart.setOption(data)
}).finally(() => {this.loading = false})
}
}, },
} methods: {
getBaseInfo() {
return getSingleSiteBaseInfo(this.siteId).then((response) => {
this.info = response?.data || {};
});
},
getRunningInfo() {
return getDzjkHomeView(this.siteId).then((response) => {
this.runningInfo = response?.data || {};
});
},
init() {
this.loading = true;
// 功率曲线
this.$refs.activeChart.init(this.siteId);
// 一周冲放曲线
this.$refs.weekChart.init(this.siteId);
// 静态信息 this.getBaseInfo()
// 总累计运行数据+故障告警 this.getRunningInfo()
Promise.all([this.getBaseInfo(), this.getRunningInfo()]).finally(() => {
this.loading = false;
});
},
},
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.row-container {
& > .el-col {
margin-bottom: 20px;
}
}
//数据概览 //数据概览
.sjgl-data{ .sjgl-data {
text-align: center; text-align: center;
margin-top:20px; &:nth-child(1),
.sjgl-title{ &:nth-child(2) {
margin-bottom: 25px;
}
.sjgl-title {
color: #666666;
line-height: 14px;
}
.sjgl-value {
color: rgba(51, 51, 51, 1);
font-size: 26px;
line-height: 26px;
font-weight: 500;
margin-top: 14px;
word-wrap: break-word;
}
}
</style>
<style lang="scss">
.home-normal-info {
font-size: 12px;
.el-descriptions-item__container {
.el-descriptions-item__label {
color: #666666; color: #666666;
line-height: 14px;
padding-top: 18px;
} }
.sjgl-value{ .el-descriptions-item__content {
color: rgba(51,51,51,1); color: #333333;
font-size: 26px;
line-height: 26px;
font-weight: 500;
margin-top: 14px;
} }
} }
//实时告警 }
.ssgj-container{ /* card标题里的时间选择器 */
padding:20px; .time-range-card {
height: 310px; &.common-card-container .el-card__header {
box-sizing: border-box; padding-top: 0;
::v-deep{ padding-bottom: 0;
.el-table .el-table__header-wrapper th, .el-table .el-table__fixed-header-wrapper th{ .time-range-header {
background:#FFF2CB ; height: 40px;
display: flex;
justify-content: space-between;
align-items: center;
.card-title {
line-height: 40px;
} }
} }
} }
}
</style> </style>

View File

@ -2,7 +2,7 @@
<template> <template>
<!-- 6个方块--> <!-- 6个方块-->
<el-row :gutter="10"> <el-row :gutter="10">
<el-col :xs="12" :sm="8" :lg="4" class="single-square-box-container" v-for="(item,index) in singleZdSqaure" :key="index+'singleSquareBox'"> <el-col :xs="12" :sm="8" :lg="4" style="margin-bottom: 10px;" class="single-square-box-container" v-for="(item,index) in singleZdSqaure" :key="index+'singleSquareBox'">
<single-square-box :data="{...item,value:formatNumber(data[item.attr])}" ></single-square-box> <single-square-box :data="{...item,value:formatNumber(data[item.attr])}" ></single-square-box>
</el-col> </el-col>
</el-row> </el-row>
@ -64,5 +64,10 @@ export default {
<style scoped lang="scss"> <style scoped lang="scss">
@media only screen and (min-width: 1200px) {
.single-square-box-container {
min-width: 16.6666666667%;
width: fit-content;
}
}
</style> </style>

View File

@ -61,6 +61,10 @@
<span>{{scope.row.maxCellVoltage}} V</span> <span>{{scope.row.maxCellVoltage}} V</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column
prop="maxCellVoltageId"
label="电池号码">
</el-table-column>
<el-table-column <el-table-column
prop="minVoltage" prop="minVoltage"
label="单体最低电压"> label="单体最低电压">
@ -68,12 +72,20 @@
<span>{{scope.row.minCellVoltage}} V</span> <span>{{scope.row.minCellVoltage}} V</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column
prop="minCellVoltageId"
label="电池号码">
</el-table-column>
<el-table-column <el-table-column
label="单体最高温度"> label="单体最高温度">
<template slot-scope="scope"> <template slot-scope="scope">
<span>{{scope.row.maxCellTemp}} &#8451;</span> <span>{{scope.row.maxCellTemp}} &#8451;</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column
prop="maxCellTempId"
label="电池号码">
</el-table-column>
<el-table-column <el-table-column
prop="minTemperature" prop="minTemperature"
label="单体最低温度"> label="单体最低温度">
@ -81,6 +93,10 @@
<span>{{scope.row.minCellTemp}} &#8451;</span> <span>{{scope.row.minCellTemp}} &#8451;</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column
prop="minCellTempId"
label="电池号码">
</el-table-column>
</el-table> </el-table>
</el-card> </el-card>

View File

@ -1,134 +1,140 @@
<template> <template>
<div v-loading="loading"> <div v-loading="loading">
<el-card shadow="always" class="common-card-container" :class="zbInfo.emsCommunicationStatus === '1' ? 'zb-common-card-container' : 'cnb-common-card-container'"> <el-card
<div slot="header"> shadow="always"
<span class="large-title">1#{{zbInfo.deviceName}}</span> class="common-card-container"
<div class="status"> :class="
<div>{{$store.state.ems.communicationStatusOptions[zbInfo.emsCommunicationStatus]}}</div> zbInfo.emsCommunicationStatus !== '0'
<div>数据更新时间{{zbInfo.dataUpdateTime}}</div> ? 'zb-common-card-container'
</div> : 'cnb-common-card-container'
"
>
<div slot="header">
<span class="large-title">1#{{ zbInfo.deviceName }}</span>
<div class="status">
<div>
{{
$store.state.ems.communicationStatusOptions[
zbInfo.emsCommunicationStatus
]
}}
</div> </div>
<el-table <div>数据更新时间{{ zbInfo.dataUpdateTime }}</div>
class="common-table" </div>
:data="zbInfo.loadDataDetailInfo"
stripe
style="width: 100%;">
<el-table-column
prop="category"
label="类别">
</el-table-column>
<el-table-column
prop="totalKwh"
label="总/kWh"
>
</el-table-column>
<el-table-column
prop="peakKwh"
label="尖/kWh">
</el-table-column>
<el-table-column
prop="highKwh"
label="峰/kWh">
</el-table-column>
<el-table-column
prop="flatKwh"
label="平/kWh">
</el-table-column>
<el-table-column
prop="valleyKwh"
label="谷/kWh">
</el-table-column>
</el-table>
</el-card>
<el-card shadow="always" class="common-card-container" style="margin-top:20px" :class="cnbInfo.emsCommunicationStatus === '1' ? 'zb-common-card-container' : 'cnb-common-card-container'">
<div slot="header">
<span class="large-title">2#{{cnbInfo.deviceName}}</span>
<div class="status">
<div>{{$store.state.ems.communicationStatusOptions[cnbInfo.emsCommunicationStatus]}}</div>
<div>数据更新时间{{cnbInfo.dataUpdateTime}}</div>
</div>
</div>
<el-table
class="common-table"
:data="cnbInfo.meteDataDetailInfo"
stripe
style="width: 100%;">
<el-table-column
prop="category"
label="类别">
</el-table-column>
<el-table-column
prop="activePower"
label="有功功率"
>
</el-table-column>
<el-table-column
prop="reactivePower"
label="无功功率">
</el-table-column>
</el-table>
</el-card>
</div> </div>
<el-table
class="common-table"
:data="zbInfo.loadDataDetailInfo"
stripe
style="width: 100%"
>
<el-table-column prop="category" label="类别"> </el-table-column>
<el-table-column prop="totalKwh" label="总/kWh"> </el-table-column>
<el-table-column prop="peakKwh" label="尖/kWh"> </el-table-column>
<el-table-column prop="highKwh" label="峰/kWh"> </el-table-column>
<el-table-column prop="flatKwh" label="平/kWh"> </el-table-column>
<el-table-column prop="valleyKwh" label="谷/kWh"> </el-table-column>
</el-table>
</el-card>
<el-card
shadow="always"
class="common-card-container"
style="margin-top: 20px"
:class="
cnbInfo.emsCommunicationStatus !== '0'
? 'zb-common-card-container'
: 'cnb-common-card-container'
"
>
<div slot="header">
<span class="large-title">2#{{ cnbInfo.deviceName }}</span>
<div class="status">
<div>
{{
$store.state.ems.communicationStatusOptions[
cnbInfo.emsCommunicationStatus
]
}}
</div>
<div>数据更新时间{{ cnbInfo.dataUpdateTime }}</div>
</div>
</div>
<el-table
class="common-table"
:data="cnbInfo.meteDataDetailInfo"
stripe
style="width: 100%"
>
<el-table-column prop="category" label="类别"> </el-table-column>
<el-table-column prop="activePower" label="有功功率"> </el-table-column>
<el-table-column prop="reactivePower" label="无功功率">
</el-table-column>
</el-table>
</el-card>
</div>
</template> </template>
<script> <script>
import getQuerySiteId from "@/mixins/ems/getQuerySiteId"; import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
import {getAmmeterDataList} from '@/api/ems/dzjk' import { getAmmeterDataList } from "@/api/ems/dzjk";
export default { export default {
name:'DzjkSbjkDb', name: "DzjkSbjkDb",
mixins:[getQuerySiteId], mixins: [getQuerySiteId],
data() { data() {
return { return {
loading:false, loading: false,
zbInfo:{}, zbInfo: {},
cnbInfo:{}, cnbInfo: {},
} };
}, },
methods:{ methods: {
init(){ init() {
this.loading = true this.loading = true;
getAmmeterDataList(this.siteId).then(response => { getAmmeterDataList(this.siteId)
this.zbInfo =JSON.parse(JSON.stringify(response?.data?.ammeterLoadData || {})); .then((response) => {
this.cnbInfo =JSON.parse(JSON.stringify(response?.data?.ammeterMeteData || {})); this.zbInfo = JSON.parse(
}).finally(() => {this.loading = false}) JSON.stringify(response?.data?.ammeterLoadData || {})
} );
this.cnbInfo = JSON.parse(
JSON.stringify(response?.data?.ammeterMeteData || {})
);
})
.finally(() => {
this.loading = false;
});
},
}, },
mounted(){ mounted() {},
};
}
}
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.zb-common-card-container,.cnb-common-card-container{ .zb-common-card-container,
::v-deep{ .cnb-common-card-container {
.el-card__header{ ::v-deep {
padding:10px 14px; .el-card__header {
background-color: #FC6B69; padding: 10px 14px;
color:#ffffff; background-color: #fc6b69;
color: #ffffff;
position: relative; position: relative;
} }
} }
} }
.cnb-common-card-container{ .cnb-common-card-container {
margin-top:25px; margin-top: 25px;
::v-deep{ ::v-deep {
.el-card__header{ .el-card__header {
background-color: #05AEA3; background-color: #05aea3;
} }
} }
} }
.status{ .status {
position: absolute; position: absolute;
right:14px; right: 14px;
top:50%; top: 50%;
transform: translateY(-50%); transform: translateY(-50%);
color: #ffffff; color: #ffffff;
font-size: 12px; font-size: 12px;
line-height: 20px; line-height: 20px;
} }
</style> </style>

View File

@ -20,8 +20,9 @@
start-placeholder="开始时间" start-placeholder="开始时间"
value-format="yyyy-MM-dd" value-format="yyyy-MM-dd"
:picker-options="pickerOptions" :picker-options="pickerOptions"
:default-value="defaultDateRange" end-placeholder="结束时间"
end-placeholder="结束时间"> :clearable="false"
>
</el-date-picker> </el-date-picker>
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
@ -29,145 +30,181 @@
<el-button @click="onReset">重置</el-button> <el-button @click="onReset">重置</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
<div id="lineChart" style="height: 360px;width: 100%;"></div> <div id="lineChart" style="height: 360px; width: 100%"></div>
</div> </div>
</el-dialog> </el-dialog>
</template> </template>
<script> <script>
import * as echarts from 'echarts' import * as echarts from "echarts";
import resize from '@/mixins/ems/resize' import resize from "@/mixins/ems/resize";
import {getSingleBatteryData} from '@/api/ems/dzjk' import { getSingleBatteryData } from "@/api/ems/dzjk";
import { formatDate } from "@/filters/ems";
export default { export default {
mixins: [resize], mixins: [resize],
data() { data() {
return { return {
loading: false, loading: false,
siteId:'', siteId: "",
deviceId:'', deviceId: "",
clusterDeviceId:'', clusterDeviceId: "",
dataType:'',//展示的数据类型 空值展示所有数据 dataType: "", //展示的数据类型 空值展示所有数据
pickerOptions:{ pickerOptions: {
disabledDate(time) { disabledDate(time) {
return time.getTime() > Date.now(); return time.getTime() > Date.now();
}, },
}, },
dialogTableVisible: false, dialogTableVisible: false,
dateRange: [], dateRange: [],
defaultDateRange:[] defaultDateRange: [],
} };
}, },
methods: { methods: {
handleColsed(done){ resetDefaultDateRange() {
if (!this.chart) { const now = new Date(),
return done() formatNow = formatDate(now);
} const weekAgo = formatDate(
this.chart.dispose() new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000)
this.chart = null );
done() this.dateRange = [weekAgo, formatNow];
this.defaultDateRange = [weekAgo, formatNow];
}, },
getData(){ handleColsed(done) {
if(this.loading) return if (!this.chart) {
return done();
}
this.chart.dispose();
this.chart = null;
done();
},
getData() {
if (this.loading) return;
this.loading = true; this.loading = true;
this.chart.showLoading() this.chart.showLoading();
const {siteId, deviceId,clusterDeviceId,dateRange:[startDate='',endDate='']}=this; const {
getSingleBatteryData({siteId, deviceId,clusterDeviceId,startDate,endDate}).then(response => { siteId,
this.setOption(response?.data || []) deviceId,
}).finally(()=>{ clusterDeviceId,
this.loading = false; dateRange: [startDate = "", endDate = ""],
this.chart.hideLoading() } = this;
getSingleBatteryData({
siteId,
deviceId,
clusterDeviceId,
startDate,
endDate,
}) })
.then((response) => {
this.setOption(response?.data || []);
})
.finally(() => {
this.loading = false;
this.chart.hideLoading();
});
}, },
// 重置 // 重置
onReset(){ onReset() {
this.dateRange=[] this.dateRange = this.defaultDateRange;
this.getData() this.getData();
}, },
initChart({siteId, clusterDeviceId, deviceId},dataType) { initChart({ siteId, clusterDeviceId, deviceId }, dataType) {
this.siteId=siteId this.siteId = siteId;
this.clusterDeviceId=clusterDeviceId this.clusterDeviceId = clusterDeviceId;
this.deviceId=deviceId this.deviceId = deviceId;
this.dataType=dataType this.dataType = dataType;
this.dateRange=[] this.resetDefaultDateRange();
this.dialogTableVisible = true this.dialogTableVisible = true;
this.$nextTick(()=>{ this.$nextTick(() => {
!this.chart && (this.chart = echarts.init(document.querySelector('#lineChart'))) !this.chart &&
this.getData() (this.chart = echarts.init(document.querySelector("#lineChart")));
}) this.getData();
});
}, },
setOption(data) { setOption(data) {
const obj = { const obj = {
voltage:'电压', voltage: "电压",
temperature:'温度', temperature: "温度",
soc:'SOC', soc: "SOC",
soh:'SOH', soh: "SOH",
} };
let source,series,{dataType} = this let source,
if(dataType){ series,
source = [['日期',obj[dataType]]] { dataType } = this;
data.forEach(item => { if (dataType) {
source.push([item.dataTimestamp,item[dataType]]) source = [["日期", obj[dataType]]];
}) data.forEach((item) => {
series=[{ source.push([item.dataTimestamp, item[dataType]]);
name:obj[dataType], });
type: 'line', series = [
}]
}else{
source = [['日期','电压','温度','SOC','SOH']]
data.forEach(item => {
source.push([item.dataTimestamp,item.voltage,item.temperature,item.soc,item.soh])
})
series=[
{ {
name:'电压', name: obj[dataType],
type: 'line', type: "line",
},
},{ ];
name:'温度', } else {
type: 'line', source = [["日期", "电压", "温度", "SOC", "SOH"]];
data.forEach((item) => {
source.push([
item.dataTimestamp,
item.voltage,
item.temperature,
item.soc,
item.soh,
]);
});
series = [
{
name: "电压",
type: "line",
}, },
{ {
name:'SOC', name: "温度",
type: 'line', type: "line",
},
},{ {
name:'SOH', name: "SOC",
type: 'line', type: "line",
}] },
{
name: "SOH",
type: "line",
},
];
} }
this.chart &&
this.chart && this.chart.setOption({ this.chart.setOption({
color:['#FFBD00','#3C81FF','#05AEA3','#F86F70'], color: ["#FFBD00", "#3C81FF", "#05AEA3", "#F86F70"],
legend: { grid: {
bottom: '10', containLabel: true,
}, },
tooltip: { legend: {
trigger: 'axis', left: "center",
axisPointer: { // 坐标轴指示器,坐标轴触发有效 bottom: "15",
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow' },
} tooltip: {
}, trigger: "axis",
textStyle:{ axisPointer: {
color:"#333333", // 坐标轴指示器,坐标轴触发有效
}, type: "shadow", // 默认为直线,可选为:'line' | 'shadow'
xAxis: { },
type: 'category', },
}, textStyle: {
yAxis: { color: "#333333",
type: 'value', },
}, xAxis: {
dataset:{ type: "category",
source },
}, yAxis: {
series type: "value",
}) },
} dataset: {
source,
},
series,
});
},
}, },
mounted(){ mounted() {},
const now = new Date(); };
const lastMonth = new Date(now.getFullYear(), now.getMonth() - 1, 1);
this.defaultDateRange = [lastMonth, now];
}
}
</script> </script>

View File

@ -0,0 +1,191 @@
<template>
<div>
<template v-if="totalSize.length === 0">
<el-empty :size="200"></el-empty>
</template>
<template v-else>
<div class="lists-container clearfix">
<div
class="lists"
v-for="(item, index) in tableData"
:key="index + 'dtdcList'"
:class="handleListClass(item)"
@click="chartDetail(item)"
>
<div style="font-size: 10px; font-weight: 600">
{{ item.clusterDeviceId }}
</div>
<div>#{{ item.deviceId }}</div>
<div class="dy">{{ item.voltage }}V</div>
<div class="wd">{{ item.temperature }}</div>
</div>
</div>
<!-- <el-pagination
v-show="tableData.length > 0"
background
@size-change="(val) => $emit('handleSizeChange', val)"
@current-change="(val) => $emit('handleCurrentChange', val)"
:current-page="pageNum"
:page-size="pageSize"
:page-sizes="[10, 20, 30, 40]"
layout="total, sizes, prev, pager, next, jumper"
:total="totalSize"
style="margin-top: 15px; text-align: center"
>
</el-pagination> -->
</template>
</div>
</template>
<script>
export default {
props: {
pointIdList: {
require: true,
type: Object,
default: () => {
return {};
},
},
tableData: {
require: true,
type: Array,
default: () => {
return [];
},
},
totalSize: {
require: true,
type: Number,
default: 0,
},
// pageNum: {
// require: true,
// type: Number,
// default: 1,
// },
// pageSize: {
// require: true,
// type: Number,
// default: 10,
// },
},
data() {
return {
//最低单体温度 最高温度 最低电压 最高电压 todo 这里的顺序需要和图形组件里的顺序保持一致,
colorMap: {
0: "minwd",
1: "maxwd",
2: "mindy",
3: "maxdy",
},
};
},
methods: {
//处理图形class 对应高亮设置
handleListClass(item) {
let className = "";
const { clusterDeviceId, deviceId } = item,
clusterIdList = Object.keys(this.pointIdList);
if (clusterIdList.includes(clusterDeviceId)) {
const index = this.pointIdList[clusterDeviceId].findIndex(
(ids) => ids === parseInt(deviceId)
);
if (index > -1) {
className = this.colorMap[index];
}
}
return className;
},
//查看表格行图表
chartDetail(row, dataType = "") {
const { clusterDeviceId, deviceId } = row;
this.$emit("chart", { clusterDeviceId, deviceId, dataType });
},
},
};
</script>
<style lang="scss" scoped>
.lists-container {
padding: 20px 0;
.lists {
margin: 10px 5px;
padding: 5px 9px;
font-size: 11px;
line-height: 20px;
border: 1.6px solid #09ada3;
border-radius: 5px;
position: relative;
color: #333333;
float: left;
box-sizing: content-box;
min-width: 60px;
width: auto;
cursor: pointer;
&::before {
display: block;
content: "";
top: -7px;
left: 50%;
transform: translateX(-50%);
position: absolute;
width: 45%;
height: 0;
border-bottom: 7px solid #09ada3;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
}
&.minwd {
border-color: #3794ff;
.wd {
color: #3794ff;
}
&::before {
border-bottom-color: #3794ff;
}
}
&.maxwd {
border-color: #ff3a3b;
.wd {
color: #ff3a3b;
}
&::before {
border-bottom-color: #ff3a3b;
}
}
&.mindy {
border-color: #de6902;
.dy {
color: #de6902;
}
&::before {
border-bottom-color: #de6902;
}
}
&.maxdy {
border-color: #ffb521;
.dy {
color: #ffb521;
}
&::before {
border-bottom-color: #ffb521;
}
}
}
}
.dtdc-pagination {
::v-deep {
.el-button {
padding: 2px 10px !important;
font-size: 11px;
line-height: 16px;
}
.activeBtn {
background-color: #09ada3;
border-color: #09ada3;
}
}
}
</style>

View File

@ -0,0 +1,125 @@
<template>
<div>
<el-table
class="common-table"
:data="tableData"
stripe
style="width: 100%; margin-top: 25px"
>
<el-table-column prop="deviceId" label="单体编号"> </el-table-column>
<el-table-column prop="clusterDeviceId" label="簇号"> </el-table-column>
<el-table-column prop="voltage" label="电压V">
<template slot-scope="scope">
<el-button
@click="chartDetail(scope.row, 'voltage')"
type="text"
size="small"
>
{{ scope.row.voltage }}
</el-button>
</template>
</el-table-column>
<el-table-column prop="temperature" label="温度(℃)">
<template slot-scope="scope">
<el-button
@click="chartDetail(scope.row, 'temperature')"
type="text"
size="small"
>
{{ scope.row.temperature }}
</el-button>
</template>
</el-table-column>
<el-table-column prop="soc" label="SOC%">
<template slot-scope="scope">
<el-button
@click="chartDetail(scope.row, 'soc')"
type="text"
size="small"
>
{{ scope.row.soc }}
</el-button>
</template>
</el-table-column>
<el-table-column prop="soh" label="SOH%">
<template slot-scope="scope">
<el-button
@click="chartDetail(scope.row, 'soh')"
type="text"
size="small"
>
{{ scope.row.soh }}
</el-button>
</template>
</el-table-column>
<el-table-column label="曲线图">
<template slot-scope="scope">
<el-button @click="chartDetail(scope.row)" type="text" size="small">
展示
</el-button>
</template>
</el-table-column>
</el-table>
<!-- <el-pagination
v-show="tableData.length > 0"
background
@size-change="(val) => $emit('handleSizeChange', val)"
@current-change="(val) => $emit('handleCurrentChange', val)"
:current-page="pageNum"
:page-size="pageSize"
:page-sizes="[10, 20, 30, 40]"
layout="total, sizes, prev, pager, next, jumper"
:total="totalSize"
style="margin-top: 15px; text-align: center"
>
</el-pagination> -->
</div>
</template>
<script>
export default {
props: {
tableData: {
require: true,
type: Array,
default: () => {
return [];
},
},
pointIdList: {
require: true,
type: Object,
default: () => {
return {};
},
},
totalSize: {
require: true,
type: Number,
default: 0,
},
// pageNum: {
// require: true,
// type: Number,
// default: 1,
// },
// pageSize: {
// require: true,
// type: Number,
// default: 10,
// },
},
data() {
return {};
},
methods: {
//查看表格行图表
chartDetail(row, dataType = "") {
const { clusterDeviceId, deviceId } = row;
this.$emit("chart", { clusterDeviceId, deviceId, dataType });
},
},
};
</script>
<style></style>

View File

@ -1,224 +1,335 @@
<template> <template>
<el-card v-loading="loading" shadow="always" class="common-card-container common-card-container-no-title-bg"> <el-card
<div slot="header"> v-loading="loading"
<span class="large-title">单体电池实时数据</span> shadow="always"
</div> class="common-card-container common-card-container-no-title-bg"
<!-- 搜索栏--> >
<el-form :inline="true" class="select-container"> <div slot="header">
<el-form-item label="电池堆"> <span class="large-title">单体电池实时数据</span>
<el-select v-model="search.stackId" placeholder="请选择" @change="changeStackId"> </div>
<el-option :label="item.deviceName" :value="item.id" v-for="(item,index) in stackOptions" :key="index+'stackOptions'"></el-option> <!-- 搜索栏-->
</el-select> <el-form :inline="true" class="select-container">
</el-form-item> <el-form-item label="编号">
<el-form-item label="电池簇"> <el-input
<el-select v-model="search.clusterId" :no-data-text="!search.stackId && stackOptions.length > 0 ? '请先选择电池堆':'无数据'" placeholder="请选择" :loading="clusterloading" loading-text="正在加载数据"> v-model="search.batteryId"
<el-option :label="item.deviceName" :value="item.id" v-for="(item,index) in clusterOptions" :key="index+'clusterOptions'"></el-option> placeholder="请输入"
</el-select> clearable
</el-form-item> style="width: 150px"
<el-form-item> />
<el-button type="primary" @click="onSearch" native-type="button">搜索</el-button> </el-form-item>
</el-form-item> <el-form-item label="电池堆">
<el-form-item> <el-select
<el-button @click="onReset" native-type="button">重置</el-button> v-model="search.stackId"
</el-form-item> placeholder="请选择"
</el-form> @change="changeStackId"
<!-- 图表--> >
<!-- <div style="margin:30px 0;box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);">--> <el-option
<!-- <el-row style="background:#fff;margin:30px 0;">--> :label="item.deviceName"
<!-- <el-col :xs="24" :sm="24" :lg="24">--> :value="item.id"
<!-- <bar-chart ref="barChart"/>--> v-for="(item, index) in stackOptions"
<!-- </el-col>--> :key="index + 'stackOptions'"
<!-- </el-row>--> ></el-option>
<!-- </div>--> </el-select>
<el-table </el-form-item>
class="common-table" <el-form-item label="电池簇">
:data="tableData" <el-select
stripe v-model="search.clusterId"
style="width: 100%;margin-top: 25px"> :no-data-text="
<el-table-column !search.stackId && stackOptions.length > 0
prop="deviceId" ? '请先选择电池堆'
label="单体编号"> : '无数据'
</el-table-column> "
<el-table-column placeholder="请选择"
prop="clusterDeviceId" :loading="clusterloading"
label="簇号"> loading-text="正在加载数据"
</el-table-column> >
<el-table-column <el-option
prop="voltage" :label="item.deviceName"
label="电压V" :value="item.id"
> v-for="(item, index) in clusterOptions"
<template slot-scope="scope"> :key="index + 'clusterOptions'"
<el-button ></el-option>
@click="chartDetail(scope.row,'voltage')" </el-select>
type="text" </el-form-item>
size="small"> <el-form-item>
{{scope.row.voltage}} <el-button type="primary" @click="onSearch" native-type="button"
</el-button> >搜索</el-button
</template> >
</el-table-column> </el-form-item>
<el-table-column <el-form-item>
prop="temperature" <el-button @click="onReset" native-type="button">重置</el-button>
label="温度(℃)"> </el-form-item>
<template slot-scope="scope"> </el-form>
<el-button <!-- 切换 -->
@click="chartDetail(scope.row,'temperature')" <div class="tip-container">
type="text" <div class="color-tip" v-show="activeBtn === 'list'">
size="small"> 单体信息
{{scope.row.temperature}} <span class="tip minwd">最低单体温度</span>
</el-button> <span class="tip maxwd">最高单体温度</span>
</template> <span class="tip mindy">单体最低电压</span>
</el-table-column> <span class="tip maxdy">单体最高电压</span>
<el-table-column </div>
prop="soc" <el-button-group class="ems-btns-group">
label="SOC%"> <el-button
<template slot-scope="scope"> :class="{ activeBtn: activeBtn === 'table' }"
<el-button @click="changeMenu('table')"
@click="chartDetail(scope.row,'soc')" >图表</el-button
type="text" >
size="small"> <el-button
{{scope.row.soc}} :class="{ activeBtn: activeBtn === 'list' }"
</el-button> @click="changeMenu('list')"
</template> >图形</el-button
</el-table-column> >
<el-table-column </el-button-group>
prop="soh" </div>
label="SOH%"> <component
<template slot-scope="scope"> :is="activeBtn === 'table' ? 'DtdcTable' : 'DtdcList'"
<el-button :tableData="tableData"
@click="chartDetail(scope.row,'soh')" :totalSize="totalSize"
type="text" :pointIdList="pointIdList"
size="small"> @chart="chartDetail"
{{scope.row.soh}} ></component>
</el-button> <el-pagination
</template> v-show="tableData.length > 0"
</el-table-column> background
<el-table-column @size-change="handleSizeChange"
label="曲线图"> @current-change="handleCurrentChange"
<template slot-scope="scope"> :current-page="pageNum"
<el-button :page-size="pageSize"
@click="chartDetail(scope.row)" :page-sizes="[10, 20, 30, 40]"
type="text" layout="total, sizes, prev, pager, next, jumper"
size="small"> :total="totalSize"
展示 style="margin-top: 15px; text-align: center"
</el-button> >
</template> </el-pagination>
</el-table-column> <chart-detail ref="chartDetail" />
</el-table> </el-card>
<el-pagination
v-show="tableData.length>0"
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="pageNum"
:page-size="pageSize"
:page-sizes="[10, 20, 30, 40]"
layout="total, sizes, prev, pager, next, jumper"
:total="totalSize"
style="margin-top:15px;text-align: center"
>
</el-pagination>
<chart-detail ref="chartDetail"/>
</el-card>
</template> </template>
<script> <script>
import BarChart from './BarChart' import BarChart from "./BarChart";
import {getStackNameList, getClusterNameList, getClusterDataInfoList} from '@/api/ems/dzjk' import {
getStackNameList,
getClusterNameList,
getClusterDataInfoList,
} from "@/api/ems/dzjk";
import getQuerySiteId from "@/mixins/ems/getQuerySiteId"; import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
import ChartDetail from "./ChartDetail.vue"; import ChartDetail from "./ChartDetail.vue";
import Table from "./Table.vue";
import List from "./List.vue";
export default { export default {
name:'DzjkSbjkDtdc', name: "DzjkSbjkDtdc",
mixins:[getQuerySiteId], mixins: [getQuerySiteId],
components:{BarChart, ChartDetail}, components: { BarChart, ChartDetail, DtdcTable: Table, DtdcList: List },
computed: {
pointIdList() {
let obj = {};
this.pointData.forEach((item) => {
const {
maxCellTempId,
maxCellVoltageId,
minCellTempId,
minCellVoltageId,
} = item;
obj[item.clusterId] = [
parseInt(minCellTempId || 0),
parseInt(maxCellTempId || 0),
parseInt(minCellVoltageId || 0),
parseInt(maxCellVoltageId || 0),
]; //最低单体温度 最高温度 最低电压 最高电压 todo 这里的顺序需要和图形组件里的顺序保持一致,
});
return obj;
},
},
data() { data() {
return { return {
loading:false, loading: false,
clusterloading:false, clusterloading: false,
search:{stackId:'',clusterId:''}, search: { stackId: "", clusterId: "", batteryId: "" },
stackOptions:[],//{id:'',deviceName:''} stackOptions: [], //{id:'',deviceName:''}
clusterOptions:[],//{id:'',deviceName:''} clusterOptions: [], //{id:'',deviceName:''}
tableData:[], tableData: [],
pageSize:10,//分页栏当前每个数据总数 pointData: [],
pageNum:1,//分页栏当前 pageSize: 40, //分页栏当前每个数据总
totalSize:0,//table表格数据总 pageNum: 1, //分页栏当前页
} totalSize: 0, //table表格数据总数
activeBtn: "table",
};
}, },
methods:{ methods: {
changeMenu(menu) {
const { activeBtn } = this;
activeBtn !== menu && (this.activeBtn = menu);
},
//查看表格行图表 //查看表格行图表
chartDetail(row,dataType = ''){ chartDetail({ clusterDeviceId, deviceId, dataType = "" }) {
const { clusterDeviceId, deviceId} = row,{siteId} = this const { siteId } = this;
this.$refs.chartDetail.initChart({siteId,clusterDeviceId,deviceId},dataType) this.$refs.chartDetail.initChart(
{ siteId, clusterDeviceId, deviceId },
dataType
);
}, },
// 分页 // 分页
handleSizeChange(val) { handleSizeChange(val) {
this.pageSize = val; this.pageSize = val;
this.$nextTick(()=>{ this.$nextTick(() => {
this.getTableData() this.getTableData();
}) });
}, },
handleCurrentChange(val) { handleCurrentChange(val) {
this.pageNum = val this.pageNum = val;
this.$nextTick(()=>{ this.$nextTick(() => {
this.getTableData() this.getTableData();
}) });
}, },
// 搜索 // 搜索
onSearch(){ onSearch() {
this.pageNum =1//每次搜索从1开始搜索 this.pageNum = 1; //每次搜索从1开始搜索
this.getTableData() this.getTableData();
}, },
// 重置 // 重置
// 清空搜索栏选中数据 // 清空搜索栏选中数据
// 清空电池簇列表,保留电池堆列表 // 清空电池簇列表,保留电池堆列表
onReset(){ onReset() {
this.search={stackId:'',clusterId:''} this.search = { stackId: "", clusterId: "", batteryId: "" };
this.clusterOptions=[] this.clusterOptions = [];
this.pageNum = 1 this.pageNum = 1;
this.getTableData() this.getTableData();
}, },
changeStackId(val){ changeStackId(val) {
if(val){ if (val) {
console.log('选择了电池堆,需要获取对应的电池簇',val,this.search.stackId) console.log(
this.search.clusterId='' "选择了电池堆需要获取对应的电池簇",
this.getClusterList() val,
this.search.stackId
);
this.search.clusterId = "";
this.getClusterList();
} }
}, },
//表格数据 //表格数据
getTableData(){ getTableData() {
this.loading=true; this.loading = true;
const {stackId:stackDeviceId,clusterId:clusterDeviceId} =this.search const {
const {siteId,pageNum,pageSize}=this stackId: stackDeviceId,
getClusterDataInfoList({stackDeviceId,clusterDeviceId,siteId,pageNum,pageSize}).then(response => { clusterId: clusterDeviceId,
this.tableData=response?.rows || []; batteryId,
this.totalSize = response?.total || 0 } = this.search;
}).finally(()=>{ const { siteId, pageNum, pageSize } = this;
this.loading=false; getClusterDataInfoList({
stackDeviceId,
clusterDeviceId,
siteId,
batteryId,
pageNum,
pageSize,
}) })
.then((response) => {
this.tableData = response?.rows?.[0]?.batteryList || []; //todo check
this.pointData = response?.rows?.[0]?.clusterList || []; //todo check
this.totalSize = response?.total || 0;
})
.finally(() => {
this.loading = false;
});
}, },
getStackList(){ getStackList() {
getStackNameList(this.siteId).then(response => { getStackNameList(this.siteId).then((response) => {
this.stackOptions = JSON.parse(JSON.stringify(response?.data || [])) this.stackOptions = JSON.parse(JSON.stringify(response?.data || []));
});
},
getClusterList() {
this.clusterloading = true;
getClusterNameList({
stackDeviceId: this.search.stackId,
siteId: this.siteId,
}) })
.then((response) => {
this.clusterOptions = JSON.parse(
JSON.stringify(response?.data || [])
);
})
.finally(() => {
this.clusterloading = false;
});
}, },
getClusterList(){ init() {
this.clusterloading =true
getClusterNameList(this.search.stackId).then(response => {
this.clusterOptions = JSON.parse(JSON.stringify(response?.data || []))
}).finally(() => {this.clusterloading =false})
},
init(){
// 只有页面初次加载或切换站点的时候调用电池堆列表,其他情况不需要 // 只有页面初次加载或切换站点的时候调用电池堆列表,其他情况不需要
this.search={stackId:'',clusterId:''}//保证切换站点时,清空选择项 this.search = { stackId: "", clusterId: "", batteryId: "" }; //保证切换站点时,清空选择项
this.getStackList() this.clusterOptions = [];
this.getTableData() this.pageNum = 1;
} this.totalSize = 0;
this.getStackList();
this.getTableData();
},
}, },
mounted(){ mounted() {},
};
</script>
<style scoped lang="scss">
.tip-container {
text-align: right;
position: relative;
.color-tip {
position: absolute;
top: 50%;
left: 0;
transform: translateY(-50%);
font-size: 11px;
line-height: 12px;
color: #333;
.tip {
padding-left: 30px;
position: relative;
&::before {
display: block;
content: "";
position: absolute;
left: 14px;
top: 50%;
transform: translateY(-50%);
width: 8px;
height: 8px;
border-radius: 50%;
}
&.minwd {
color: #3794ff;
&::before {
background: #3794ff;
}
}
&.maxwd {
color: #ff3a3b;
&::before {
background: #ff3a3b;
}
}
&.mindy {
color: #de6902;
&::before {
background: #de6902;
}
}
&.maxdy {
color: #ffb521;
&::before {
background: #ffb521;
}
}
}
}
::v-deep {
.el-button-group.ems-btns-group {
& > .el-button {
padding: 5px 30px !important;
font-size: 11px;
line-height: 16px;
// padding-left: 50px;
// padding-right: 50px;
// font-size: 16px;
// line-height: 24px;
}
}
} }
} }
</script> </style>

View File

@ -1,5 +1,7 @@
<template> <template>
<div class="ems-dashboard-editor-container ems-content-container-padding sbjk-ems-dashboard-editor-container"> <div
class="ems-dashboard-editor-container ems-content-container-padding sbjk-ems-dashboard-editor-container ems-third-menu-container"
>
<el-menu <el-menu
class="ems-third-menu" class="ems-third-menu"
:default-active="$route.name" :default-active="$route.name"
@ -7,50 +9,58 @@
text-color="#666666" text-color="#666666"
active-text-color="#ffffff" active-text-color="#ffffff"
> >
<el-menu-item :index="item.name" v-for="(item,index) in childrenRoute" :key="index+'dzjkChildrenRoute'"> <el-menu-item
<router-link style="height: 100%;width: 100%;display: block" :to="{path:item.path,query:$route.query}"> :index="item.name"
{{item.meta.title}} v-for="(item, index) in childrenRoute"
:key="index + 'dzjkChildrenRoute'"
>
<router-link
style="height: 100%; width: 100%; display: block"
:to="{ path: item.path, query: $route.query }"
>
{{ item.meta.title }}
</router-link> </router-link>
</el-menu-item> </el-menu-item>
</el-menu> </el-menu>
<div class="ems-content-container ems-content-container-padding sbjk-ems-content-container"> <div
class="ems-content-container ems-content-container-padding sbjk-ems-content-container"
>
<keep-alive> <keep-alive>
<router-view></router-view> <router-view></router-view>
</keep-alive> </keep-alive>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import { dzjk } from '@/router/ems' import { dzjk } from "@/router/ems";
const childrenRoute = dzjk[0].children.find(item=> item.name==='DzjkSbjk').children//获取到单站监控-设备监控下面的字路由 const childrenRoute = dzjk[0].children.find(
console.log('设备监控子路由',childrenRoute) (item) => item.name === "DzjkSbjk"
).children; //获取到单站监控-设备监控下面的字路由
console.log("设备监控子路由", childrenRoute);
export default { export default {
name:'DzjkSbjk', name: "DzjkSbjk",
data(){ data() {
return { return {
childrenRoute, childrenRoute,
activeMenu:'' activeMenu: "",
} };
}, },
mounted() { mounted() {
console.log('当前设备监控页面路由',this.$route) console.log("当前设备监控页面路由", this.$route);
} },
};
}
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.sbjk-ems-dashboard-editor-container{ .sbjk-ems-dashboard-editor-container {
display: flex; display: flex;
background: #FFFFFF; background: #ffffff;
} }
.sbjk-ems-content-container{ .sbjk-ems-content-container {
margin-top:0; margin-top: 0;
padding-top:0; padding-top: 0;
padding-right: 0; padding-right: 0;
flex: 1; flex: 1;
} }
</style> </style>

View File

@ -12,10 +12,11 @@
<div>{{$store.state.ems.communicationStatusOptions[pcsItem.communicationStatus]}}</div> <div>{{$store.state.ems.communicationStatusOptions[pcsItem.communicationStatus]}}</div>
<div>数据更新时间{{pcsItem.dataUpdateTime}}</div> <div>数据更新时间{{pcsItem.dataUpdateTime}}</div>
</div> </div>
<!-- <div class="pcs-btns">--> <div class="pcs-btns">
<!-- <el-button type="warning" size="small" @click="problemSaved">故障复位</el-button>--> <el-badge :value="pcsItem.alarmNum || 0" class="item">
<!-- <el-button size="small" @click="machineClosed">关机</el-button>--> <i class="el-icon-message-solid" style="font-size: 26px;color: #fff;display: block;"></i>
<!-- </div>--> </el-badge>
</div>
</el-header> </el-header>
<el-main style="padding: 0"> <el-main style="padding: 0">
<div class="descriptions-main"> <div class="descriptions-main">
@ -91,11 +92,8 @@ export default {
if (action === 'confirm') { if (action === 'confirm') {
instance.confirmButtonLoading = true; instance.confirmButtonLoading = true;
setTimeout(() => { setTimeout(() => {
// todo 调用接口如果关机成功 调用done方法 否则不关闭弹窗
done(); done();
// setTimeout(() => {
instance.confirmButtonLoading = false; instance.confirmButtonLoading = false;
// }, 300);
}, 3000); }, 3000);
} else { } else {
done(); done();
@ -122,11 +120,8 @@ export default {
if (action === 'confirm') { if (action === 'confirm') {
instance.confirmButtonLoading = true; instance.confirmButtonLoading = true;
setTimeout(() => { setTimeout(() => {
// todo 调用接口如果关机成功 调用done方法 否则不关闭弹窗
done(); done();
// setTimeout(() => {
instance.confirmButtonLoading = false; instance.confirmButtonLoading = false;
// }, 300);
}, 3000); }, 3000);
} else { } else {
done(); done();

View File

@ -57,7 +57,7 @@ export default {
top: '10', top: '10',
}, },
grid: { grid: {
left: "15%" containLabel: true
}, },
tooltip: { tooltip: {
trigger: 'axis', trigger: 'axis',

View File

@ -61,7 +61,7 @@ export default {
} }
}, },
grid: { grid: {
left: "15%" containLabel: true
}, },
textStyle:{ textStyle:{
color:"#333333", color:"#333333",

View File

@ -62,7 +62,7 @@ export default {
} }
}, },
grid: { grid: {
left: "15%" containLabel: true
}, },
textStyle:{ textStyle:{
color:"#333333", color:"#333333",

View File

@ -58,7 +58,7 @@ export default {
} }
}, },
grid: { grid: {
left: "15%" containLabel: true
}, },
textStyle:{ textStyle:{
color:"#333333", color:"#333333",

View File

@ -1,7 +1,6 @@
<template> <template>
<div v-loading="loading"> <div v-loading="loading">
<!-- todo 判断条件是否需要更新-->
<div class="yl-item-container" :class="{'yl-warn-item-container':item.workMode !== '0'}" v-for="(item,index) in list" :key="index+'ylLise'"> <div class="yl-item-container" :class="{'yl-warn-item-container':item.workMode !== '0'}" v-for="(item,index) in list" :key="index+'ylLise'">
<div class="header"> <div class="header">
<div class="header-title">{{item.systemName}}</div> <div class="header-title">{{item.systemName}}</div>

View File

@ -125,9 +125,12 @@ export default {
}) })
this.chart.setOption({ this.chart.setOption({
color:['#FFBD00','#3C81FF'], color:['#FFBD00','#3C81FF'],
grid: {
containLabel: true
},
legend: { legend: {
left: 'center', left: 'center',
bottom: '10', bottom: '15',
}, },
tooltip: { tooltip: {
trigger: 'axis', trigger: 'axis',

View File

@ -171,7 +171,7 @@ export default {
}, },
async getClusterList(){ async getClusterList(){
this.clusterloading =true this.clusterloading =true
await getClusterNameList(this.search.stackId).then(response => { await getClusterNameList({stackDeviceId: this.search.stackId, siteId: this.siteId}).then(response => {
const data = JSON.parse(JSON.stringify(response?.data || [])) const data = JSON.parse(JSON.stringify(response?.data || []))
this.clusterOptions = data this.clusterOptions = data
this.search.clusterId = data.length > 0 ? data[0].id : '' this.search.clusterId = data.length > 0 ? data[0].id : ''

View File

@ -121,9 +121,12 @@ export default {
}) })
this.chart.setOption({ this.chart.setOption({
color:['#FFBD00','#3C81FF'], color:['#FFBD00','#3C81FF'],
grid: {
containLabel: true
},
legend: { legend: {
left: 'center', left: 'center',
bottom: '10', bottom: '15',
}, },
tooltip: { tooltip: {
trigger: 'axis', trigger: 'axis',

View File

@ -31,7 +31,7 @@
<div>总放电量:<span class="point">{{totalDisChargedCap | formatNumber}}kWh</span></div> <div>总放电量:<span class="point">{{totalDisChargedCap | formatNumber}}kWh</span></div>
<div>综合效率:<span class="point">{{efficiency | formatNumber}}%</span></div> <div>综合效率:<span class="point">{{efficiency | formatNumber}}%</span></div>
</div> </div>
<div id="dlzbChart" style="height: 310px"></div> <div id="dlzbChart" style="height: 310px;"></div>
</div> </div>
</el-card> </el-card>
</template> </template>
@ -73,16 +73,21 @@ export default {
}) })
this.chart.setOption({ this.chart.setOption({
color:['#FFBD00','#3C81FF','#05AEA3'], color:['#FFBD00','#3C81FF','#05AEA3'],
// legend: { legend: {
// left: 'right', left: 'center',
// bottom: '10', bottom: '15',
// },
tooltip: {},
xAxis: {
type: 'category',
name:unit,
nameLocation:'center'
}, },
grid: {
top:40,
containLabel: true
},
tooltip: {},
xAxis: [{
type: 'category',
name:`单位:${unit}`,
nameLocation:'center',
nameGap:30
}],
yAxis: [{ yAxis: [{
type: 'value', type: 'value',
name:'充电量/放电量kWh', name:'充电量/放电量kWh',
@ -102,7 +107,6 @@ export default {
onZero:false onZero:false
} }
}], }],
grid:{top:40},
dataset:{ dataset:{
source source
}, },
@ -163,7 +167,7 @@ export default {
line-height: 18px; line-height: 18px;
color: #333333; color: #333333;
font-size: 16px; font-size: 16px;
padding:10px 0; padding:20px 0;
>div{ >div{
display: inline-block; display: inline-block;
margin-right: 20px; margin-right: 20px;

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="ems-dashboard-editor-container ems-content-container-padding tjbb-ems-dashboard-editor-container"> <div class="ems-dashboard-editor-container ems-third-menu-container">
<el-menu <el-menu
class="ems-third-menu" class="ems-third-menu"
:default-active="$route.name" :default-active="$route.name"
@ -7,51 +7,55 @@
text-color="#666666" text-color="#666666"
active-text-color="#ffffff" active-text-color="#ffffff"
> >
<el-menu-item :index="item.name" v-for="(item,index) in childrenRoute" :key="index+'tjbbChildrenRoute'"> <el-menu-item
<router-link style="height: 100%;width: 100%;display: block;" :to="{path:item.path,query:$route.query}"> :index="item.name"
{{item.meta.title}} v-for="(item, index) in childrenRoute"
:key="index + 'tjbbChildrenRoute'"
>
<router-link
style="height: 100%; width: 100%; display: block"
:to="{ path: item.path, query: $route.query }"
>
{{ item.meta.title }}
</router-link> </router-link>
</el-menu-item> </el-menu-item>
</el-menu> </el-menu>
<div class="ems-content-container ems-content-container-padding tjbb-ems-content-container"> <div
class="ems-content-container ems-content-container-padding tjbb-ems-content-container"
>
<keep-alive> <keep-alive>
<router-view></router-view> <router-view></router-view>
</keep-alive> </keep-alive>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import { dzjk } from '@/router/ems' import { dzjk } from "@/router/ems";
const childrenRoute = dzjk[0].children.find(item=> item.name==='DzjkTjbb').children//获取到统计报表下面的字路由 const childrenRoute = dzjk[0].children.find(
console.log('设备监控子路由',childrenRoute) (item) => item.name === "DzjkTjbb"
).children; //获取到统计报表下面的字路由
console.log("设备监控子路由", childrenRoute);
export default { export default {
name:'DzjkTjbb', name: "DzjkTjbb",
data(){ data() {
return { return {
childrenRoute, childrenRoute,
activeMenu:'' activeMenu: "",
} };
}, },
mounted() { mounted() {
console.log('当前统计报表页面路由',this.$route) console.log("当前统计报表页面路由", this.$route);
} },
};
}
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.tjbb-ems-dashboard-editor-container{ .tjbb-ems-content-container {
display: flex; margin-top: 0;
background: #FFFFFF; padding-top: 0;
}
.tjbb-ems-content-container{
margin-top:0;
padding-top:0;
padding-right: 0; padding-right: 0;
flex: 1; flex: 1;
} }
</style> </style>

View File

@ -128,9 +128,12 @@ export default {
}) })
this.chart.setOption({ this.chart.setOption({
color:['#FFBD00','#3C81FF','#91cc74'], color:['#FFBD00','#3C81FF','#91cc74'],
grid: {
containLabel: true
},
legend: { legend: {
left: 'center', left: 'center',
bottom: '10', bottom: '15',
}, },
tooltip: { tooltip: {
trigger: 'axis', trigger: 'axis',

View File

@ -0,0 +1,577 @@
<template>
<div class="ems-dashboard-editor-container" v-loading="loading">
<div class="container" v-show="!empty">
<!-- 电脑 -->
<div class="top">
<div class="cloud-container">
<div class="cloud">
<span style="z-index: 2; position: relative"></span>
</div>
</div>
<div class="double-arrows">
<div class="top-arrows"></div>
<div class="bottom-arrows"></div>
</div>
<div class="computer">
<img src="@/assets/images/ems/computer.png" alt="" />
<span style="z-index: 2; position: relative">ems</span>
</div>
</div>
<div class="outer-border">
<!-- 电表-->
<div class="row-lists-container" v-if="showDb">
<div class="row-title">电表({{ db.length }})</div>
<div class="row-lists">
<div v-for="item in db" :key="item.deviceId" class="row-items">
<div
class="status"
:class="
item.communicationStatus === '0' ? 'status-running' : ''
"
>
{{ communicationStatusOptions[item.communicationStatus] }}
</div>
<div class="row-items-img">
<img
class="img-db"
:src="require('@/assets/images/ems/db.png')"
/>
<div class="name">{{ item.deviceName }}</div>
</div>
</div>
</div>
</div>
<!-- 液冷-->
<div class="row-lists-container" v-if="showLq">
<div class="row-title">冷却({{ lq.length }})</div>
<div class="row-lists">
<div v-for="item in lq" :key="item.deviceId" class="row-items">
<div
class="status"
:class="
item.communicationStatus === '0' ? 'status-running' : ''
"
>
{{ communicationStatusOptions[item.communicationStatus] }}
</div>
<div class="row-items-img">
<img
class="img-lq"
:src="require('@/assets/images/ems/lq.png')"
/>
<div class="name">{{ item.deviceName }}</div>
</div>
</div>
</div>
</div>
<!-- PCS-->
<div class="row-lists-container" v-if="showPcs">
<div class="row-lists">
<div class="row-title">PCS({{ pcs.length }})</div>
<div
v-for="(item, index) in pcs"
:key="item.deviceId"
class="row-items row-items-pcs"
>
<!-- pcs -->
<div class="parent-dash">
<div
class="status"
:class="
item.communicationStatus === '0' ? 'status-running' : ''
"
>
{{ communicationStatusOptions[item.communicationStatus] }}
</div>
<div class="row-items-img">
<img
class="img-pcs"
:src="require('@/assets/images/ems/pcs.png')"
/>
<div class="name">{{ item.deviceName }}</div>
</div>
</div>
<!-- 子设备 bms -->
<div
v-if="item.children && item.children.length > 0"
class="children-dash"
>
<div
class="row-children-title"
v-if="bmsHasParentLength > 0 && index === 0"
>
BMS({{ bmsHasParentLength }})
</div>
<div
v-for="(childrenItem, childrenIndex) in item.children"
:key="childrenIndex + 'childrenBms'"
class="children-dash-items"
>
<div
class="status"
:class="
childrenItem.communicationStatus === '0'
? 'status-running'
: ''
"
>
{{
communicationStatusOptions[
childrenItem.communicationStatus
]
}}
</div>
<div class="row-items-img">
<img
class="img-pcs"
:src="require('@/assets/images/ems/bms.png')"
/>
<div class="name">{{ childrenItem.deviceName }}</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row-lists-container" v-if="showPcs">
<div class="row-title">PCS({{ pcs.length }})</div>
<div class="row-lists">
<div
v-for="item in bmsNoParent"
:key="item.deviceId"
class="row-items row-items-pcs"
>
<!-- pcs -->
<div class="parent-dash">
<div
class="status"
:class="
item.communicationStatus === '0' ? 'status-running' : ''
"
>
{{ communicationStatusOptions[item.communicationStatus] }}
</div>
<div class="row-items-img">
<img :src="require('@/assets/images/ems/bms.png')" />
<div class="name">{{ item.deviceName }}</div>
</div>
</div>
</div>
</div>
</div>
<!-- bms没有上级设备-->
<div class="row-lists-container" v-if="bmsNoParent.length > 0">
<div class="row-title">BMS({{ bmsNoParent.length }})</div>
<div class="row-lists">
<div
v-for="item in bmsNoParent"
:key="item.deviceId"
class="row-items row-items-pcs"
>
<!-- pcs -->
<div class="parent-dash">
<div
class="status"
:class="
item.communicationStatus === '0' ? 'status-running' : ''
"
>
{{ communicationStatusOptions[item.communicationStatus] }}
</div>
<div class="row-items-img">
<img :src="require('@/assets/images/ems/bms.png')" />
<div class="name">{{ item.deviceName }}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<el-empty v-show="empty" :image-size="200"></el-empty>
</div>
</template>
<script>
import { getDeviceList } from "@/api/ems/site";
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
import { mapState } from "vuex";
export default {
name: "DzjkZxlt",
mixins: [getQuerySiteId],
data() {
return {
loading: false,
pcs: [],
bms: [],
db: [],
lq: [],
pcsHasChildren: [],
pcsNoChildren: [],
bmsNoParent: [],
};
},
computed: {
...mapState({
communicationStatusOptions: (state) =>
state.ems.communicationStatusOptions,
}),
showPcs() {
return this.pcs.length > 0;
},
showBms() {
return this.bms.length > 0;
},
showDb() {
return this.db.length > 0;
},
showLq() {
return this.lq.length > 0;
},
bmsHasParentLength() {
let count = 0;
this.pcs.forEach((item) => (count += item.children.length));
return count;
},
empty() {
return !this.showBms && !this.showPcs && !this.showDb && !this.showLq;
},
},
methods: {
init() {
this.pcs = [];
this.bms = [];
this.lq = [];
this.db = [];
this.bmsNoParent = [];
this.loading = true;
getDeviceList(this.siteId)
.then((response) => {
const data = JSON.parse(JSON.stringify(response?.data || []));
let pcs = [],
bms = [],
db = [],
lq = [],
bmsNoParent = [];
data.forEach((item) => {
// 电表
if (item.deviceCategory === "AMMETER") {
db.push({ ...item, children: [] });
} else if (item.deviceCategory === "PCS") {
// pcs
pcs.push({ ...item, children: [] });
} else if (item.deviceCategory === "STACK") {
// bms
bms.push({ ...item, children: [] });
} else if (item.deviceCategory === "COOLING") {
// 液冷
lq.push({ ...item, children: [] });
}
});
bms.forEach((item, index) => {
if (item.parentId) {
pcs
.find((pcsItem) => pcsItem.deviceId === item.parentId)
.children.push(item);
} else {
bmsNoParent.push(item);
}
});
this.pcs = pcs;
this.bms = bms;
this.lq = lq;
this.db = db;
this.pcsHasChildren = pcs.filter((item) => item.children.length > 0);
this.pcsNoChildren = pcs.filter((item) => item.children.length === 0);
this.bmsNoParent = bmsNoParent;
})
.finally(() => {
this.loading = false;
});
},
},
};
</script>
<style lang="scss" scoped>
$sqDistance: 30px;
$borderColor: #174a8e;
$lineColor: #86bcc7;
.ems-dashboard-editor-container {
background-color: #ffffff;
padding: 0;
color: #666666;
.container {
display: flex;
position: relative;
}
//云 、计算机 、箭头
.top {
z-index: 2;
width: fit-content;
display: flex;
justify-content: center;
align-items: center;
// position: absolute;
// top: 50%;
// left: 0;
// transform: translateY(-50%);
//云 样式
.cloud-container {
margin: 0 auto;
.cloud {
width: 60px;
height: 26px;
background: #cbebfd;
border-radius: 100px;
position: relative;
text-align: center;
font-weight: bold;
font-size: 14px;
line-height: 26px;
}
.cloud:before,
.cloud:after {
content: "";
position: absolute;
background: #cbebfd;
width: 30px;
height: 30px;
border-radius: 100%;
}
.cloud:before {
top: -9px;
left: 8px;
}
.cloud:after {
top: -6px;
right: 9px;
}
}
//双箭头
.double-arrows {
height: fit-content;
margin: 0 10px;
text-align: center;
.top-arrows,
.bottom-arrows {
height: 4px;
width: 30px;
background-color: #5ea9df;
margin: 0 10px;
position: relative;
&::after {
content: "";
position: absolute;
left: 0;
width: 0;
height: 0;
}
}
.top-arrows {
vertical-align: super;
}
.top-arrows::after {
top: -4px;
border-bottom: 6px solid transparent;
border-left: 6px solid transparent;
border-right: 6px solid #5ea9df;
border-top: 6px solid transparent;
left: -11px;
}
.bottom-arrows {
margin-top: 8px;
&::after {
top: -4px;
border-top: 6px solid transparent;
border-left: 6px solid #5ea9df;
border-right: 6px solid transparent;
border-bottom: 6px solid transparent;
right: -11px;
left: auto;
}
}
}
//电脑
.computer {
text-align: center;
font-size: 14px;
line-height: 16px;
font-weight: bold;
position: relative;
background: #fff;
img {
width: 80px;
height: auto;
display: block;
}
}
}
.outer-border {
position: relative;
width: fit-content;
border: 1.5px solid $borderColor;
border-radius: 5px;
padding-left: 120px;
padding-right: 20px;
margin-left: -40px;
}
// 设备列表
.row-lists-container {
font-size: 10px;
position: relative;
padding: 10px;
.row-title {
position: absolute;
left: -$sqDistance - 30px;
top: calc(50% + 10px);
transform: translateY(-50%);
color: #000;
font-weight: bolder;
}
.row-lists {
display: flex;
position: relative;
.row-items {
position: relative;
padding: 5px 0;
&:not(:first-child) {
margin-left: $sqDistance; //和外层父元素上下padding一致
}
&::before {
content: "";
display: block;
height: 3px;
width: $sqDistance - 2px;
background: $lineColor;
position: absolute;
left: -$sqDistance;
top: calc(50% + 10px);
transform: scale(1, 0.4);
}
// 一列 第一个设备最上面的线
&:first-child {
&::before {
width: $sqDistance + 20px;
// top: -$sqDistance - 20px;
}
}
// 一列 最后一个设备最下面的线
// &:last-child {
// &::after {
// content: "";
// display: block;
// width: 3px;
// height: $sqDistance - 2px;
// background: $lineColor;
// position: absolute;
// bottom: -$sqDistance;
// left: 50%;
// transform: scale(0.4, 1);
// }
// }
// 设备状态
.status {
margin: 0 auto 4px;
width: fit-content;
height: 18px;
padding: 0 8px;
box-sizing: border-box;
text-align: center;
font-size: 8px;
line-height: 18px;
border: 1px solid #08ffff;
border-radius: 2px;
background: #aaaaaa;
color: #ffffff;
&.status-running {
background: #00c69c;
}
}
// 图片+设备名称
.row-items-img {
position: relative;
padding-top: 12px;
img {
width: 80px;
height: auto;
display: block;
&.img-lq {
width: 50px;
}
&.img-pcs {
width: 50px;
}
&.img-db {
width: 56px;
}
}
.name {
position: absolute;
top: 1px;
left: 0;
color: #666;
white-space: nowrap;
}
}
}
}
}
//子设备
.row-lists-container-children {
margin: 10px 0 0 $sqDistance;
.parent-dash {
position: relative;
&::before {
content: "";
display: block;
height: 40px;
width: 3px;
background: #ec7f8c;
position: absolute;
left: 20%;
top: -40px;
transform: scale(0.4, 1) rotate(-40deg);
}
}
}
.parent-dash {
width: fit-content;
}
.children-dash {
margin: $sqDistance 0 0 $sqDistance;
position: relative;
.row-children-title {
position: absolute;
left: -$sqDistance - 30px;
top: calc(50% + 10px);
transform: translateY(-50%);
color: #000;
font-weight: bolder;
}
.children-dash-items {
position: relative;
&::before {
content: "";
display: block;
height: $sqDistance;
width: 3px;
background: #ec7f8c;
position: absolute;
left: 20%;
top: -$sqDistance;
transform: scale(0.4, 1) rotate(-40deg);
}
}
}
}
</style>

View File

@ -0,0 +1,614 @@
<template>
<div class="ems-dashboard-editor-container" v-loading="loading" >
<div class="container" v-show="!empty">
<div class="top">
<div class="cloud-container">
<div class="cloud">
<span style="z-index:2;position: relative;"></span>
</div>
</div>
<div class="double-arrows">
<div class="top-arrows"></div>
<div class="bottom-arrows"></div>
</div>
<div class="computer">
<img src="@/assets/images/ems/computer.png" alt="">
<span style="z-index:2;position: relative;">ems</span>
</div>
<div class="arrow"></div>
</div>
<div class="bottom">
<!-- 四列设备-->
<div class="zxlt-row">
<!-- bmspcs 下级和上级在一列 -->
<div class="row-lists pcs-row-lists" v-if="showPcsAndBms">
<div class="item-square">
<div class="row-lists-title" v-if="showPcs">PCS({{pcs.length}})</div>
<div class="row-lists-title" v-if="showBms">BMS({{bms.length}})</div>
</div>
<!-- 上下级块 class区分-->
<div class="item-square pcs-has-children-item-square" :class="{'no-bms-list':!showBms}" v-for="(item,index) in pcsHasChildren" :key="index+'pcsHasChildren'">
<!-- 左边的上级 上级只有一个-->
<div class="item-lists parent-item-lists">
<!-- 上级设备-->
<div class="items normal-items-arrow">
<div class="items-inner">
<div style="text-align: center;margin-bottom:10px;">
<div class="status" :class="item.communicationStatus === '0' ?'status-normal' : 'status-warn'">通讯状态:{{communicationStatusOptions[item.communicationStatus] || '-'}}</div>
</div>
<img v-if="item.pictureUrl" :src="item.pictureUrl">
<img v-else :src="require('@/assets/images/ems/pcs.png')"/>
<div class="name">{{item.deviceName}}</div>
</div>
</div>
</div>
<!-- 右边的下级 下级有多个-->
<div class="item-lists children-item-lists">
<!-- 下级设备 循环生成-->
<div class="items children-items-arrow bms-children-arrow" v-for="children in item.children" :key="children.deviceId">
<div class="items-inner">
<div style="text-align: center;margin-bottom:10px;">
<div class="status" :class="children.communicationStatus === '0' ?'status-normal' : 'status-warn'">通讯状态:{{communicationStatusOptions[children.communicationStatus] || '-'}}</div>
</div>
<img v-if="children.pictureUrl" :src="children.pictureUrl">
<img v-else :src="require('@/assets/images/ems/bms.png')"/>
<div class="name">{{children.deviceName}}</div>
</div>
</div>
</div>
</div>
<!-- 没有上下级关系的bmspcs-->
<div class="item-square" :class="{'no-bms-list':!showBms}">
<!-- 左边没有下级的pcs-->
<div class="item-lists">
<div class="items normal-items-arrow" v-for="item in pcsNoChildren" :key="item.deviceId">
<div class="items-inner">
<div style="text-align: center;margin-bottom:10px;">
<div class="status" :class="item.communicationStatus === '0' ?'status-normal' : 'status-warn'">通讯状态:{{communicationStatusOptions[item.communicationStatus] || '-'}}</div>
</div>
<img v-if="item.pictureUrl" :src="item.pictureUrl">
<img v-else :src="require('@/assets/images/ems/pcs.png')"/>
<div class="name">{{item.deviceName}}</div>
</div>
</div>
</div>
<!-- 右边没有上级的bms-->
<div class="item-lists">
<!-- 下级设备 循环生成-->
<div class="items children-items-arrow" v-for="item in bmsNoParent" :key="item.deviceId">
<div class="items-inner">
<div style="text-align: center;margin-bottom:10px;">
<div class="status" :class="item.communicationStatus === '0' ?'status-normal' : 'status-warn'">通讯状态:{{communicationStatusOptions[item.communicationStatus] || '-'}}</div>
</div>
<img v-if="item.pictureUrl" :src="item.pictureUrl">
<img v-else :src="require('@/assets/images/ems/bms.png')"/>
<div class="name">{{item.deviceName}}</div>
</div>
</div>
</div>
</div>
</div>
<!-- 电表-->
<div class="row-lists" v-if="showDb">
<div class="item-square">
<div class="row-lists-title" style="width:100%;">电表({{db.length}})</div>
</div>
<div class="item-square">
<!-- 左边的下级 下级有多个-->
<div class="item-lists">
<!-- 下级设备 循环生成-->
<div class="items normal-items-arrow" v-for="item in db" :key="item.deviceId">
<div class="items-inner">
<div style="text-align: center;margin-bottom:10px;">
<div class="status" :class="item.communicationStatus === '0' ?'status-normal' : 'status-warn'">通讯状态:{{communicationStatusOptions[item.communicationStatus] || '-'}}</div>
</div>
<img v-if="item.pictureUrl" :src="item.pictureUrl">
<img v-else :src="require('@/assets/images/ems/bms.png')"/>
<div class="name">{{item.deviceName}}</div>
</div>
</div>
</div>
</div>
</div>
<!--冷却-->
<div class="row-lists" v-if="showLq">
<div class="item-square">
<div class="row-lists-title" style="width:100%;">冷却({{lq.length}})</div>
</div>
<div class="item-square">
<div class="item-lists">
<div class="items normal-items-arrow" v-for="item in lq" :key="item.deviceId">
<div class="items-inner">
<div style="text-align: center;margin-bottom:10px;">
<div class="status" :class="item.communicationStatus === '0' ?'status-normal' : 'status-warn'">通讯状态:{{communicationStatusOptions[item.communicationStatus] || '-'}}</div>
</div>
<img v-if="item.pictureUrl" :src="item.pictureUrl">
<img v-else :src="require('@/assets/images/ems/bms.png')"/>
<div class="name">{{item.deviceName}}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<el-empty v-show="empty" :image-size="200"></el-empty>
</div>
</template>
<script>
import {getDeviceList} from'@/api/ems/site'
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
import {mapState} from "vuex";
export default {
name: 'DzjkZxlt',
mixins: [getQuerySiteId],
data() {
return {
loading:false,
pcs :[],
bms:[],
db:[],
lq:[],
pcsHasChildren:[],
pcsNoChildren:[],
bmsNoParent:[]
}
},
computed:{
...mapState({
communicationStatusOptions:(state)=>state.ems.communicationStatusOptions
}),
showPcs(){
return this.pcs.length>0
},
showBms(){
return this.bms.length>0
},
showDb(){
return this.db.length>0
},
showLq(){
return this.lq.length>0
},
showPcsAndBms(){
return this.showPcs || this.showBms
},
empty(){
return !this.showBms && !this.showPcs && !this.showDb && !this.showLq
},
},
methods: {
init(){
this.pcs = []
this.bms = []
this.lq=[]
this.db=[]
this.bmsNoParent=[]
this.loading = true
getDeviceList(this.siteId).then(response => {
const data =JSON.parse(JSON.stringify(response?.data || []))
let pcs = [],bms=[],db=[],lq=[],bmsNoParent=[]
data.forEach(item=>{
// 电表
if(item.deviceCategory === 'AMMETER'){
db.push({...item,children:[]})
}else if(item.deviceCategory === 'PCS'){
// pcs
pcs.push({...item,children:[]})
}else if(item.deviceCategory === 'STACK'){
// bms
bms.push({...item,children:[]})
}else if(item.deviceCategory === 'COOLING'){
// 液冷
lq.push({...item,children:[]})
}
})
bms.forEach((item,index)=>{
if(item.parentId){
pcs.find(pcsItem=>pcsItem.deviceId === item.parentId).children.push(item)
}else{
bmsNoParent.push(item)
}
})
this.pcs = pcs
this.bms = bms
this.lq=lq
this.db=db
this.pcsHasChildren = pcs.filter(item=>item.children.length > 0)
this.pcsNoChildren = pcs.filter(item=>item.children.length === 0)
this.bmsNoParent = bmsNoParent
}).finally(() => {
this.loading = false
})
}
},
}
</script>
<style lang="scss" scoped>
$distance:60px;
$arrowDistance:80px;//margin:60+quare的padding10
$arrowColoe:#5ea9df;
$lineColoe:#5ea9df;
.ems-dashboard-editor-container {
background-color: #ffffff;
padding:0;
.container{
position: relative;
overflow-x: auto;
}
//云 、计算机 、箭头
.top{
width: 280px;
font-size: 30px;
line-height: 40px;
font-weight: 500;
display: flex;
flex-direction: column;
//云 样式
.cloud-container{
padding-top:40px;
margin:0 auto;
.cloud {
width: 150px;
height: 60px;
background: #cbebfd;
border-radius: 200px;
position: relative;
text-align: center;
color:#666666;
}
.cloud:before, .cloud:after {
content: '';
position: absolute;
background:#cbebfd;
width: 80px;
height: 80px;
border-radius: 50%;
}
.cloud:before {
top: -28px;
left: 20px;
}
.cloud:after {
top: -31px;
right: 20px;
}
}
//双箭头
.double-arrows {
height: 50px;
margin:20px 0;
text-align: center;
.top-arrows,.bottom-arrows{
height: 100%;
width: 6px;
background-color: $arrowColoe;
display: inline-block;
margin: 0 10px;
position: relative;
vertical-align: super;
&::after {
content: '';
position: absolute;
left:0;
width: 0;
height: 0;
}
}
.top-arrows{
vertical-align: super;
}
.top-arrows::after {
bottom: -24px;
border-bottom: 12px solid transparent;
border-left: 12px solid transparent;
border-right: 12px solid transparent;
border-top: 14px solid $arrowColoe;
left: -9px;
}
.bottom-arrows{
margin-top:12px;
&::after {
top: -24px;
border-top: 12px solid transparent;
border-left: 12px solid transparent;
border-right: 12px solid transparent;
border-bottom: 14px solid $arrowColoe;
left: -9px;
}
}
}
//电脑
.computer{
margin:20px auto;
text-align: center;
color:#666666;
position: relative;
img {
width: auto;
height: 100px;
display: block;
}
}
.arrow{
height: 50px;
width: 30px;
border-radius: 5px;
background-color: $arrowColoe;
position: relative;
margin:0 auto;
&::after{
content: "";
position: absolute;
width: 0;
height: 0;
left: -9px;
border-top: 24px solid $arrowColoe;
border-left: 24px solid transparent;
border-bottom: 24px solid transparent;
border-right: 24px solid transparent;
bottom: -44px;
}
}
}
.bottom{
z-index:1;
box-sizing: border-box;
margin-top:50px;
.zxlt-row{
display: flex;
padding:20px $distance;
position: relative;
width: fit-content;
&:before{
content: '';
display: block;
width:calc(100% - 100px);
height:1px;
background-color: $lineColoe;
position:absolute;
top:0;
left: $distance/2;
}
.row-lists{
height: fit-content;
position: relative;
&:before{
content: '';
display: block;
height: 100%;
width: 1px;
position: absolute;
left:-($distance/2);
top:-20px;
background-color: $lineColoe;
}
//pcs列 bms右侧的边框
&.pcs-row-lists{
&:after{
content: '';
display: block;
height: 100%;
width: 1px;
position: absolute;
right:-(($distance/2) + 1);
top:-20px;
background-color: $lineColoe;
}
}
&:not(:last-child){
margin-right: $distance;
}
.item-square{
//左右 两列
display: flex;
vertical-align: middle;
align-items: flex-start;
padding:10px;
border-radius: 5px;
&:not(:last-child){
margin-bottom: 40px;
}
.row-lists-title{
font-size: 20px;
line-height: 20px;
color: #333333;
font-weight: 500;
text-align: center;
flex: 1;
}
.item-lists{
position: relative;
&:not(:last-child){
margin-right:$distance;
}
//每个设备
.items{
background-color: #cbebfd;
position: relative;
border-radius: 5px;
padding: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1), 0 0 0 rgba(0, 0, 0, 0.5);
//普通设备 箭头方向
&.normal-items-arrow{
&:before{
content: '';
display: block;
width:($arrowDistance/2) - 15;
height: 4px;
background-color: $arrowColoe;
position: absolute;
top:50%;
left: -($arrowDistance/2);
transform: translateY(-50%);
}
&:after{
content: '';
display: block;
height: 0;
width: 0;
border-left: 10px solid #5ea9df;
border-right: 10px solid transparent;
border-bottom: 10px solid transparent;
border-top: 10px solid transparent;
position: absolute;
top: 50%;
left: -15px;
transform: translateY(-50%);
}
}
//下级的箭头
&.children-items-arrow{
&:before{
content: '';
display: block;
width:($arrowDistance/2) - 15;
height: 4px;
background-color: $arrowColoe;
position: absolute;
top:50%;
right: -($arrowDistance/2);
transform: translateY(-50%);
}
&:after{
content: '';
display: block;
height: 0;
width: 0;
border-right: 10px solid #5ea9df;
border-left: 10px solid transparent;
border-bottom: 10px solid transparent;
border-top: 10px solid transparent;
position: absolute;
top: 50%;
right: -15px;
transform: translateY(-50%);
}
}
&:not(:last-child){
margin-bottom: 15px;
}
.items-inner{
background-color: #ffffff;
border-radius: 5px;
padding:10px;
width:130px;
text-align: center;
}
img{
width: 80px;
height: auto;
display: block;
z-index:2;
margin: 0 auto;
}
.name{
text-align: center;
margin-top:10px;
font-size: 14px;
line-height: 20px;
z-index:2;
}
.status{
z-index:2;
margin-top:10px;
font-size: 14px;
line-height: 20px;
position: relative;
padding-left:20px;
display: inline;
&.status-normal {
&:before {
content: "";
display: block;
width: 15px;
height: 15px;
border-radius: 50%;
background-color: #05AEA3;
position: absolute;
top:50%;
left:0;
transform: translate(0,-50%);
}
}
&.status-warn{
&:before{
content: "";
display: inline-block;
width: 15px;
height: 15px;
border-radius: 50%;
background-color: #FC6B69;
position: absolute;
top:50%;
left:0;
transform: translate(0,-50%);
}
}
}
}
}
.children-item-lists{
//todo 手动修改
&:before{
content: '';
display: block;
width:40px;
height: 4px;
background-color: $arrowColoe;
position: absolute;
top:50%;
left: -50px;
transform:translateY(-50%);
}
&:after{
content: '';
display: block;
height: 0;
width: 0;
border-left: 10px solid #5ea9df;
border-right: 10px solid transparent;
border-bottom: 10px solid transparent;
border-top: 10px solid transparent;
position: absolute;
top: 50%;
left: -14px;
transform:translateY(-50%);
}
}
}
.pcs-has-children-item-square{
vertical-align: middle;
align-items: center;
background-color: #ffefad;
}
.no-bms-list{
.item-lists{
&:not(:last-child){
margin-right:0;
}
}
}
}
}
}
}
</style>

File diff suppressed because it is too large Load Diff

View File

@ -39,9 +39,12 @@ export default {
source.push([item.dateMonth,item.chargeEnergy,item.disChargeEnergy]) source.push([item.dateMonth,item.chargeEnergy,item.disChargeEnergy])
}) })
this.chart.setOption({ this.chart.setOption({
grid: {
containLabel: true
},
legend: { legend: {
left: 'center', left: 'center',
bottom: '10', bottom: '15',
}, },
tooltip: { tooltip: {
trigger: 'axis', trigger: 'axis',

View File

@ -39,9 +39,12 @@ export default {
}) })
this.chart.setOption({ this.chart.setOption({
color:['#3C81FF','#FFBE29'], color:['#3C81FF','#FFBE29'],
grid: {
containLabel: true
},
legend: { legend: {
left: 'center', left: 'center',
bottom: '10', bottom: '15',
}, },
tooltip: { tooltip: {
trigger: 'axis', trigger: 'axis',

View File

@ -39,9 +39,12 @@ export default {
}) })
this.chart.setOption({ this.chart.setOption({
color:['#F86F70'], color:['#F86F70'],
grid: {
containLabel: true
},
legend: { legend: {
left: 'center', left: 'center',
bottom: '10', bottom: '15',
}, },
tooltip: { tooltip: {
trigger: 'axis', trigger: 'axis',

View File

@ -42,9 +42,12 @@ export default {
tooltip: { tooltip: {
trigger: 'item' trigger: 'item'
}, },
grid: {
containLabel: true
},
legend: { legend: {
left: 'center', left: 'center',
bottom:'10' bottom: '15',
}, },
series: [ series: [
{ {

View File

@ -39,9 +39,12 @@ export default {
}) })
this.chart.setOption({ this.chart.setOption({
color:['#FFBE01'], color:['#FFBE01'],
grid: {
containLabel: true
},
legend: { legend: {
left: 'center', left: 'center',
bottom: '10', bottom: '15',
}, },
tooltip: { tooltip: {
trigger: 'axis', trigger: 'axis',

View File

@ -3,7 +3,7 @@
<zd-info></zd-info> <zd-info></zd-info>
<div class="ems-content-container ems-content-container-padding"> <div class="ems-content-container ems-content-container-padding">
<div class="content-title">数据概览</div> <div class="content-title">数据概览</div>
<el-row :gutter="32" style="background:#fff;margin:30px 0;"> <el-row :gutter="15" style="background:#fff;margin:30px 0;">
<el-col :xs="24" :sm="12" :lg="12"> <el-col :xs="24" :sm="12" :lg="12">
<dlzb-chart ref="dlzbchart"/> <dlzb-chart ref="dlzbchart"/>
</el-col> </el-col>
@ -12,7 +12,7 @@
</el-col> </el-col>
</el-row> </el-row>
<el-row :gutter="32" style="background:#fff;margin:0;"> <el-row :gutter="15" style="background:#fff;margin:0;">
<el-col :xs="24" :sm="8" :lg="8"> <el-col :xs="24" :sm="8" :lg="8">
<gjqs-chart ref="gjqsChart"/> <gjqs-chart ref="gjqsChart"/>
</el-col> </el-col>

View File

@ -33,13 +33,11 @@
<el-option :label="item" :value="item" v-for="(item,index) in deviceCategoryList" :key="index+'deviceCategoryList'"></el-option> <el-option :label="item" :value="item" v-for="(item,index) in deviceCategoryList" :key="index+'deviceCategoryList'"></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<!-- todo 只有电池簇展示-->
<el-form-item label="上级设备" prop="parentId" v-if="formData.deviceCategory === dccDeviceCategory"> <el-form-item label="上级设备" prop="parentId" v-if="formData.deviceCategory === dccDeviceCategory">
<el-select v-model="formData.parentId" :placeholder="parentDeviceList.length === 0 && !formData.siteId ? '请先选择站点' : '请选择'" :style="{width: '100%'}"> <el-select v-model="formData.parentId" :placeholder="parentDeviceList.length === 0 && !formData.siteId ? '请先选择站点' : '请选择'" :style="{width: '100%'}">
<el-option :label="item.deviceName" :value="item.id" v-for="(item,index) in parentDeviceList" :key="index+'parentDeviceList'" ></el-option> <el-option :label="item.deviceName" :value="item.id" v-for="(item,index) in parentDeviceList" :key="index+'parentDeviceList'" ></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<!--todo tcp设备才展示ip地址端口号-->
<el-form-item label="TCP设备的ip地址" prop="ipAddress" v-if="formData.deviceType === 'TCP'"> <el-form-item label="TCP设备的ip地址" prop="ipAddress" v-if="formData.deviceType === 'TCP'">
<el-input v-model="formData.ipAddress" placeholder="请输入" clearable :style="{width: '100%'}"> <el-input v-model="formData.ipAddress" placeholder="请输入" clearable :style="{width: '100%'}">
</el-input> </el-input>

View File

@ -33,14 +33,17 @@ export default {
}) })
this.chart.setOption({ this.chart.setOption({
color:['#A796FF','#FFBE01'], color:['#A796FF','#FFBE01'],
grid: {
top:30,
containLabel: true
},
legend: { legend: {
left: 'right', left: 'right',
bottom: '10', bottom: '15',
}, },
tooltip: {}, tooltip: {},
xAxis: { type: 'category' }, xAxis: { type: 'category' },
yAxis: { }, yAxis: { },
grid:{top:30},
dataset:{ dataset:{
source source
// source:[//格式 // source:[//格式