20 Commits

Author SHA1 Message Date
84f454ea8a screen 2026-01-28 20:33:31 +08:00
ac1d1ae154 更新workStatus状态枚举 2026-01-25 16:45:33 +08:00
825243e741 电池簇备注 2026-01-25 16:36:23 +08:00
0389ed85f3 Merge branch 'develop' of http://101.43.41.9:13000/xzzn/emsfront into develop 2026-01-25 16:33:00 +08:00
aef94f406a 电池簇、电池堆工作状态更新 2026-01-25 16:30:43 +08:00
95d69fb7b1 簇枚举变更 2026-01-25 16:27:35 +08:00
684002ffc8 簇枚举变更 2026-01-25 15:12:07 +08:00
098dfa05f8 簇枚举变更 2026-01-25 14:34:01 +08:00
d6c9310e50 净置改成静置 2026-01-25 13:54:23 +08:00
142de3102b 大数据图片更新 2026-01-24 21:14:43 +08:00
9b5806a2c0 0或没有返回值隐藏alarmNum的展示 2026-01-24 10:27:43 +08:00
2b6697fa5a pointName改成 dataPointName 2026-01-24 10:02:11 +08:00
3bb859b693 BMS电池簇数据同步 2026-01-23 22:32:12 +08:00
5b3701afd0 BMS总览数据同步 2026-01-23 22:02:02 +08:00
f531075853 电表数据同步 2026-01-23 21:46:02 +08:00
8cb6fbee3e 大数据展示页面 2026-01-23 18:00:14 +08:00
f454b02c99 pcs、bmszl设备工作状态更新 2026-01-23 16:30:20 +08:00
bfe72cf2c3 首页字段、收益表格 2026-01-23 14:18:22 +08:00
fb90d81bb3 站点首页优化 2026-01-22 17:27:03 +08:00
823c0949d0 文案修改,实时运行图表切换站点数据未更新问题修复,电表未知类型 2026-01-21 18:58:37 +08:00
44 changed files with 1460 additions and 2137 deletions

View File

@ -5,9 +5,8 @@ VUE_APP_TITLE = 上动新能源-EMS管理系统
NODE_ENV = 'production' NODE_ENV = 'production'
# EMS管理系统/生产环境 # EMS管理系统/生产环境
VUE_APP_BASE_API= 'http://127.0.0.1:8089' VUE_APP_BASE_API= 'http://1.15.120.242:8089'
# EMS管理系统/生产环境 图片拼接地址 # EMS管理系统/生产环境 图片拼接地址
VUE_APP_IMG_URL = 'http://127.0.0.1:8089' VUE_APP_IMG_URL = 'http://1.15.120.242:8089'

View File

@ -10,7 +10,7 @@ import ThemePicker from "@/components/ThemePicker"
export default { export default {
name: "App", name: "App",
components: { ThemePicker }, components: { ThemePicker }
} }
</script> </script>
<style scoped> <style scoped>

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 MiB

View File

@ -4,8 +4,8 @@
//右侧内容区域 //右侧内容区域
//父元素 //父元素
.ems-dashboard-editor-container{ .ems-dashboard-editor-container {
background-color: #FFFFFF;//#F1F5FC background-color: #F1F5FC;
padding: 24px; padding: 24px;
font-size: 12px; font-size: 12px;
} }
@ -104,6 +104,10 @@
.el-card__header { .el-card__header {
background-color: #b64040; //#fc6b69; background-color: #b64040; //#fc6b69;
} }
.work-status {
color: #b64040 !important;;
}
} }
//绿色背景颜色标题 //绿色背景颜色标题
@ -111,6 +115,10 @@
.el-card__header { .el-card__header {
background-color: #40b6a5; //#05aea3; background-color: #40b6a5; //#05aea3;
} }
.work-status {
color: #40b6a5 !important;
}
} }
//灰色背景颜色标题 //灰色背景颜色标题
@ -118,6 +126,10 @@
.el-card__header { .el-card__header {
background-color: #666666; background-color: #666666;
} }
.work-status {
color: #666666 !important;;
}
} }
} }
@ -268,7 +280,7 @@
.ems-third-menu-container { .ems-third-menu-container {
position: relative; position: relative;
padding-left: 160px; padding-left: 140px;
background-color: #ffffff; background-color: #ffffff;
.ems-third-menu { .ems-third-menu {
@ -276,9 +288,10 @@
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
height: fit-content; height: fit-content;
position: absolute; position: absolute;
top:20px; top: 0;
left:20px; left: 0;
.el-menu-item{
.el-menu-item {
line-height: 45px; line-height: 45px;
height: 45px; height: 45px;
padding: 0 !important; padding: 0 !important;

View File

@ -0,0 +1,49 @@
<template>
<el-dialog :fullscreen="true" :append-to-body="true" :visible.sync="show" :show-close="false" top="0"
custom-class="big-data-dialog">
<img src="@/assets/images/ems/bigData.png" alt="">
<div class="close-btn" @click="show=false">
<i class="el-icon-close"></i>
</div>
</el-dialog>
</template>
<style lang="scss" scoped>
.close-btn {
position: absolute;
right: 10px;
top: 10px;
font-size: 23px;
line-height: 20px;
color: rgba(176, 228, 255, 0.7);
cursor: pointer;
}
img {
height: 100vh;
width: 100vw;
display: block;
margin: 0;
}
</style>
<style lang="scss">
.big-data-dialog {
.el-dialog__header {
display: none;
}
.el-dialog__body {
padding: 0;
margin: 0;
position: relative;
}
}
</style>
<script>
export default {
data() {
return {
show: false
}
}
}
</script>

View File

@ -2,6 +2,7 @@
<div class="time-range"> <div class="time-range">
<el-date-picker <el-date-picker
v-model="dateRange" v-model="dateRange"
:class="miniTimePicker ? 'mini-date-picker' : ''"
type="daterange" type="daterange"
range-separator="" range-separator=""
start-placeholder="开始时间" start-placeholder="开始时间"
@ -10,27 +11,58 @@
:picker-options="pickerOptions" :picker-options="pickerOptions"
end-placeholder="结束时间"> end-placeholder="结束时间">
</el-date-picker> </el-date-picker>
<template v-if="!showIcon">
<el-button size="mini" style="margin-left: 10px;" :loading="loading" @click="reset">重置</el-button> <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="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('before')">上一时段</el-button>
<el-button type="primary" size="mini" :loading="loading" @click="timeLine('next')" :disabled="disabledNextBtn">下一时段</el-button> <el-button type="primary" size="mini" :loading="loading" @click="timeLine('next')" :disabled="disabledNextBtn">
下一时段
</el-button>
</template>
<template v-else>
<el-button class="btn-icon" icon="el-icon-refresh-right" circle size="mini" style="margin-left: 8px;"
:loading="loading"
@click="reset"></el-button>
<el-button class="btn-icon" type="primary" size="mini" icon="el-icon-search" circle :loading="loading"
@click="search"></el-button>
<el-button class="btn-icon" type="primary" size="mini" icon="el-icon-d-arrow-left" circle :loading="loading"
@click="timeLine('before')"></el-button>
<el-button class="btn-icon" type="primary" size="mini" icon="el-icon-d-arrow-right" circle :loading="loading"
@click="timeLine('next')"
:disabled="disabledNextBtn"></el-button>
</template>
</div> </div>
</template> </template>
<script> <script>
import {formatDate} from '@/filters/ems' import {formatDate} from '@/filters/ems'
export default { export default {
computed:{ props: {
disabledNextBtn(){ showIcon: {
type: Boolean,
required: false,
default: false
},
miniTimePicker: {
type: Boolean,
required: false,
default: false
}
},
computed: {
disabledNextBtn() {
return new Date(this.dateRange[1]) >= new Date(this.defaultDateRange[1]) return new Date(this.dateRange[1]) >= new Date(this.defaultDateRange[1])
} }
}, },
data() { data() {
return { return {
loading:false, loading: false,
dateRange:[], dateRange: [],
defaultDateRange:[], defaultDateRange: [],
pickerOptions:{ pickerOptions: {
disabledDate(time) { disabledDate(time) {
return time.getTime() > Date.now(); return time.getTime() > Date.now();
}, },
@ -38,62 +70,79 @@ export default {
} }
}, },
methods: { methods: {
init(today=false){ init(today = false) {
const now = new Date(),formatNow = formatDate(now); const now = new Date(), formatNow = formatDate(now);
const weekAgo = formatDate(today ? new Date(now.getTime()) : new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000)) const weekAgo = formatDate(today ? new Date(now.getTime()) : new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000))
this.dateRange = [weekAgo, formatNow]; this.dateRange = [weekAgo, formatNow];
this.defaultDateRange=[weekAgo, formatNow]; this.defaultDateRange = [weekAgo, formatNow];
this.$emit('updateDate',this.dateRange) this.$emit('updateDate', this.dateRange)
}, },
showBtnLoading(status){ showBtnLoading(status) {
this.loading = status this.loading = status
}, },
resetDate(){ resetDate() {
this.dateRange = this.defaultDateRange this.dateRange = this.defaultDateRange
}, },
//重置 设置时间范围为初始化时间段 //重置 设置时间范围为初始化时间段
reset(){ reset() {
this.resetDate() this.resetDate()
this.$emit('reset') this.$emit('reset')
this.$emit('updateDate',this.dateRange) this.$emit('updateDate', this.dateRange)
}, },
// 搜索 // 搜索
search(){ search() {
this.$emit('updateDate',this.dateRange) this.$emit('updateDate', this.dateRange)
}, },
timeLine(type){ timeLine(type) {
if(!this.dateRange || !this.dateRange[0] || !this.dateRange[1]) return if (!this.dateRange || !this.dateRange[0] || !this.dateRange[1]) return
const nowStartTimes = new Date(this.dateRange[0]).getTime(),nowEndTimes = new Date(this.dateRange[1]).getTime(),maxTime = new Date(this.defaultDateRange[1]).getTime() const nowStartTimes = new Date(this.dateRange[0]).getTime(), nowEndTimes = new Date(this.dateRange[1]).getTime(),
maxTime = new Date(this.defaultDateRange[1]).getTime()
const nowDis = nowEndTimes - nowStartTimes//用户当前选择时间差 可能=0 const nowDis = nowEndTimes - nowStartTimes//用户当前选择时间差 可能=0
//baseTime,maxTime 毫秒数 //baseTime,maxTime 毫秒数
const baseDis = 24 * 60 * 60 * 1000 const baseDis = 24 * 60 * 60 * 1000
const calcDis = nowDis === 0 ? baseDis : nowDis const calcDis = nowDis === 0 ? baseDis : nowDis
let start = type === 'before' ? nowStartTimes - calcDis : nowStartTimes + calcDis let start = type === 'before' ? nowStartTimes - calcDis : nowStartTimes + calcDis
if(start>maxTime) start=maxTime if (start > maxTime) start = maxTime
let end = type === 'before' ? nowEndTimes - calcDis : nowEndTimes + calcDis let end = type === 'before' ? nowEndTimes - calcDis : nowEndTimes + calcDis
if(end>maxTime) end=maxTime if (end > maxTime) end = maxTime
this.dateRange = [formatDate(start),formatDate(end)] this.dateRange = [formatDate(start), formatDate(end)]
this.$emit('updateDate',this.dateRange) this.$emit('updateDate', this.dateRange)
}, },
} }
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.time-range{ .time-range {
display: flex; display: flex;
::v-deep { ::v-deep {
.el-range-editor--medium .el-range__icon, .el-range-editor--medium .el-range__close-icon{
.el-range-editor--medium .el-range__icon, .el-range-editor--medium .el-range__close-icon {
line-height: 22px; line-height: 22px;
} }
.el-range-editor--medium.el-input__inner{
.el-range-editor--medium.el-input__inner {
height: 30px; height: 30px;
} }
.el-range-editor--medium .el-range-separator{
line-height: 24px; .el-range-editor--medium .el-range-separator {
line-height: 22px;
} }
.el-button--mini{
padding:3px 10px; .el-button--mini {
padding: 3px 10px;
}
// 展示icon的小组件
.btn-icon.el-button--mini {
padding: 3px 8px;
margin-left: 6px;
}
//小宽度时间选择框
.mini-date-picker {
width: 250px !important;
} }
} }

View File

@ -32,13 +32,13 @@ export default {
attr:'installCapacity' attr:'installCapacity'
},{ },{
title:'总充电量(MWh', title:'总充电量(KWh',
num:'', num:'',
color:'#A696FF', color:'#A696FF',
attr:'totalChargedCap' attr:'totalChargedCap'
},{ },{
title:'总放电量(MWh', title:'总放电量(KWh',
num:'', num:'',
color:'#A696FF', color:'#A696FF',
attr:'totalDischargedCap' attr:'totalDischargedCap'

View File

@ -1,19 +1,24 @@
<template> <template>
<div class="navbar"> <div class="navbar">
<hamburger id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" /> <hamburger id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container"
@toggleClick="toggleSideBar"/>
<breadcrumb v-if="!topNav" id="breadcrumb-container" class="breadcrumb-container" /> <breadcrumb v-if="!topNav" id="breadcrumb-container" class="breadcrumb-container"/>
<top-nav v-if="topNav" id="topmenu-container" class="topmenu-container" /> <top-nav v-if="topNav" id="topmenu-container" class="topmenu-container"/>
<div class="right-menu"> <div class="right-menu">
<template v-if="device!=='mobile'"> <template v-if="device!=='mobile'">
<search id="header-search" class="right-menu-item" /> <div class="big-data-container">
<i class="el-icon-s-marketing big-data-icon" @click.stop="showBigDataImg"></i>
</div>
<search id="header-search" class="right-menu-item"/>
<screenfull id="screenfull" class="right-menu-item hover-effect" /> <screenfull id="screenfull" class="right-menu-item hover-effect"/>
<el-tooltip content="布局大小" effect="dark" placement="bottom"> <el-tooltip content="布局大小" effect="dark" placement="bottom">
<size-select id="size-select" class="right-menu-item hover-effect" /> <size-select id="size-select" class="right-menu-item hover-effect"/>
</el-tooltip> </el-tooltip>
</template> </template>
@ -32,16 +37,15 @@
</el-dropdown-item> </el-dropdown-item>
</el-dropdown-menu> </el-dropdown-menu>
</el-dropdown> </el-dropdown>
<div class="right-menu-item hover-effect setting" @click="setLayout" v-if="setting"> <div class="right-menu-item hover-effect setting" @click="setLayout" v-if="setting">
<svg-icon icon-class="more-up" /> <svg-icon icon-class="more-up"/>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import { mapGetters } from 'vuex' import {mapGetters} from 'vuex'
import Breadcrumb from '@/components/Breadcrumb' import Breadcrumb from '@/components/Breadcrumb'
import TopNav from '@/components/TopNav' import TopNav from '@/components/TopNav'
import Hamburger from '@/components/Hamburger' import Hamburger from '@/components/Hamburger'
@ -78,6 +82,12 @@ export default {
} }
}, },
methods: { methods: {
showBigDataImg() {
const routeUrl = this.$router.resolve({
path: '/screen'
})
window.open(routeUrl.href, '_blank')
},
toggleSideBar() { toggleSideBar() {
this.$store.dispatch('app/toggleSideBar') this.$store.dispatch('app/toggleSideBar')
}, },
@ -93,7 +103,8 @@ export default {
this.$store.dispatch('LogOut').then(() => { this.$store.dispatch('LogOut').then(() => {
location.href = '/index' location.href = '/index'
}) })
}).catch(() => {}) }).catch(() => {
})
} }
} }
} }
@ -105,7 +116,7 @@ export default {
overflow: hidden; overflow: hidden;
position: relative; position: relative;
background: #fff; background: #fff;
box-shadow: 0 1px 4px rgba(0,21,41,.08); box-shadow: 0 1px 4px rgba(0, 21, 41, .08);
.hamburger-container { .hamburger-container {
line-height: 46px; line-height: 46px;
@ -113,7 +124,7 @@ export default {
float: left; float: left;
cursor: pointer; cursor: pointer;
transition: background .3s; transition: background .3s;
-webkit-tap-highlight-color:transparent; -webkit-tap-highlight-color: transparent;
&:hover { &:hover {
background: rgba(0, 0, 0, .025) background: rgba(0, 0, 0, .025)
@ -139,6 +150,17 @@ export default {
height: 100%; height: 100%;
line-height: 50px; line-height: 50px;
.big-data-container {
display: inline-block;
padding: 0 8px;
height: 100%;
font-size: 24px;
color: #5a5e66;
vertical-align: text-bottom;
cursor: pointer;
}
&:focus { &:focus {
outline: none; outline: none;
} }
@ -177,7 +199,7 @@ export default {
border-radius: 50%; border-radius: 50%;
} }
.user-nickname{ .user-nickname {
position: relative; position: relative;
bottom: 10px; bottom: 10px;
font-size: 14px; font-size: 14px;

View File

@ -6,16 +6,16 @@ const getQuerySiteId= {
} }
}, },
watch: { watch: {
'$store.state.ems.zdList':{ '$route.query':{
handler (newQuery,oldQuery) { handler (newQuery,oldQuery) {
if(!newQuery || newQuery.length === 0){return}
// 参数变化处理 // 参数变化处理
this.$nextTick(() => { this.$nextTick(() => {
const {siteId} =newQuery[0] const {siteId} =newQuery
if(siteId){
this.siteId = siteId this.siteId = siteId
siteId && this.init(newQuery.siteId) siteId && this.init(newQuery.siteId)
console.log('watch站点列表返回数据newQuery=',newQuery) console.log('mixin=>getQuerySiteId=>页面参数siteId发生了变化,this.siteId=',this.siteId)
console.log('设置页面siteIdthis.siteId=',this.siteId) }
}) })
}, },
immediate: true, immediate: true,

View File

@ -31,7 +31,6 @@ router.beforeEach((to, from, next) => {
// 判断当前用户是否已拉取完user_info信息 // 判断当前用户是否已拉取完user_info信息
store.dispatch('GetInfo').then(() => { store.dispatch('GetInfo').then(() => {
isRelogin.show = false isRelogin.show = false
store.dispatch('getZdList')
store.dispatch('GenerateRoutes').then(accessRoutes => { store.dispatch('GenerateRoutes').then(accessRoutes => {
// 根据roles权限生成可访问的路由表 // 根据roles权限生成可访问的路由表
router.addRoutes(accessRoutes) // 动态添加可访问路由表 router.addRoutes(accessRoutes) // 动态添加可访问路由表

View File

@ -7,40 +7,55 @@ export const dzjk = [
path: '/dzjk', path: '/dzjk',
component: Layout, component: Layout,
redirect: '/dzjk/home', redirect: '/dzjk/home',
meta: {title: '单站监控', icon: 'server',}, meta: {title: '单站监控', icon: 'dashboard',},
alwaysShow: false, alwaysShow: false,
name: 'Dzjk', name: 'Dzjk',
hidden: true,
children: [
{
path: '',
component: () => import('@/views/ems/dzjk/index'),
name: 'Dzjk',
redirect: '/dzjk/home',
hidden: true,
children: [ children: [
{ {
path: '/dzjk/home', path: '/dzjk/home',
component: () => import('@/views/ems/dzjk/home/index.vue'), component: () => import('@/views/ems/dzjk/home/index.vue'),
name: 'DzjkHome', name: 'DzjkHome',
meta: {title: '站点首页', breadcrumb: false, activeMenu: '/dzjk/home', activeSecondMenuName: 'DzjkHome'} meta: {
title: '站点首页',
breadcrumb: false,
activeMenu: '/dzjk',
activeSecondMenuName: 'DzjkHome'
}
}, },
{ {
path: '/dzjk/zxlt', path: '/dzjk/zxlt',
component: () => import('@/views/ems/dzjk/zxlt/index.vue'), component: () => import('@/views/ems/dzjk/zxlt/index.vue'),
name: 'DzjkZxlt', name: 'DzjkZxlt',
meta: {title: '主线路图', breadcrumb: false, activeMenu: '/dzjk/zxlt', activeSecondMenuName: 'DzjkZxlt'} meta: {
title: '主线路图',
breadcrumb: false,
activeMenu: '/dzjk',
activeSecondMenuName: 'DzjkZxlt'
}
}, },
{ {
path: '/dzjk/sbjk', path: '/dzjk/sbjk',
component: () => import('@/views/ems/dzjk/sbjk/index.vue'), component: () => import('@/views/ems/dzjk/sbjk/index.vue'),
name: 'DzjkSbjk', name: 'DzjkSbjk',
alwaysShow: false, meta: {title: '设备监控', breadcrumb: false, activeMenu: '/dzjk'},
meta: {title: '设备监控', breadcrumb: false, activeMenu: '/dzjk/sbjk'},
hidden: false,
redirect: '/dzjk/sbjk/ssyx', redirect: '/dzjk/sbjk/ssyx',
children: [ children: [
{ {
path: 'ssyx', path: 'ssyx',
component: () => import('@/views/ems/dzjk/sbjk/ssyx/index.vue'), component: () => import('@/views/ems/dzjk/sbjk/ssyx/index.vue'),
name: 'DzjkSbjkSsyx', name: 'DzjkSbjkSsyx',
hidden: true,
meta: { meta: {
title: '实时运行', title: '实时运行',
breadcrumb: false, breadcrumb: false,
activeMenu: '/dzjk/sbjk', activeMenu: '/dzjk',
activeSecondMenuName: 'DzjkSbjk', activeSecondMenuName: 'DzjkSbjk',
deviceCategory: 'SSYX' deviceCategory: 'SSYX'
}, },
@ -49,11 +64,10 @@ export const dzjk = [
path: 'ems', path: 'ems',
component: () => import('@/views/ems/dzjk/sbjk/ems/index.vue'), component: () => import('@/views/ems/dzjk/sbjk/ems/index.vue'),
name: 'DzjkSbjkEms', name: 'DzjkSbjkEms',
hidden: true,
meta: { meta: {
title: 'EMS', title: 'EMS',
breadcrumb: false, breadcrumb: false,
activeMenu: '/dzjk/sbjk', activeMenu: '/dzjk',
activeSecondMenuName: 'DzjkSbjk', activeSecondMenuName: 'DzjkSbjk',
deviceCategory: 'EMS' deviceCategory: 'EMS'
}, },
@ -62,11 +76,10 @@ export const dzjk = [
path: 'pcs', path: 'pcs',
component: () => import('@/views/ems/dzjk/sbjk/pcs/index.vue'), component: () => import('@/views/ems/dzjk/sbjk/pcs/index.vue'),
name: 'DzjkSbjkPcs', name: 'DzjkSbjkPcs',
hidden: true,
meta: { meta: {
title: 'PCS', title: 'PCS',
breadcrumb: false, breadcrumb: false,
activeMenu: '/dzjk/sbjk', activeMenu: '/dzjk',
activeSecondMenuName: 'DzjkSbjk', activeSecondMenuName: 'DzjkSbjk',
deviceCategory: 'PCS' deviceCategory: 'PCS'
}, },
@ -75,11 +88,10 @@ export const dzjk = [
path: 'bmszl', path: 'bmszl',
component: () => import('@/views/ems/dzjk/sbjk/bmszl/index.vue'), component: () => import('@/views/ems/dzjk/sbjk/bmszl/index.vue'),
name: 'DzjkSbjkBmszl', name: 'DzjkSbjkBmszl',
hidden: true,
meta: { meta: {
title: 'BMS总览', title: 'BMS总览',
breadcrumb: false, breadcrumb: false,
activeMenu: '/dzjk/sbjk', activeMenu: '/dzjk',
activeSecondMenuName: 'DzjkSbjk', activeSecondMenuName: 'DzjkSbjk',
deviceCategory: 'STACK' deviceCategory: 'STACK'
}, },
@ -88,11 +100,10 @@ export const dzjk = [
path: 'bmsdcc', path: 'bmsdcc',
component: () => import('@/views/ems/dzjk/sbjk/bmsdcc/index.vue'), component: () => import('@/views/ems/dzjk/sbjk/bmsdcc/index.vue'),
name: 'DzjkSbjkBmsdcc', name: 'DzjkSbjkBmsdcc',
hidden: true,
meta: { meta: {
title: 'BMS电池簇', title: 'BMS电池簇',
breadcrumb: false, breadcrumb: false,
activeMenu: '/dzjk/sbjk', activeMenu: '/dzjk',
activeSecondMenuName: 'DzjkSbjk', activeSecondMenuName: 'DzjkSbjk',
deviceCategory: 'CLUSTER' deviceCategory: 'CLUSTER'
}, },
@ -101,11 +112,10 @@ export const dzjk = [
path: 'dtdc', path: 'dtdc',
component: () => import('@/views/ems/dzjk/sbjk/dtdc/index.vue'), component: () => import('@/views/ems/dzjk/sbjk/dtdc/index.vue'),
name: 'DzjkSbjkDtdc', name: 'DzjkSbjkDtdc',
hidden: true,
meta: { meta: {
title: '单体电池', title: 'BMS单体电池',
breadcrumb: false, breadcrumb: false,
activeMenu: '/dzjk/sbjk', activeMenu: '/dzjk',
activeSecondMenuName: 'DzjkSbjk', activeSecondMenuName: 'DzjkSbjk',
deviceCategory: 'BATTERY' deviceCategory: 'BATTERY'
}, },
@ -114,11 +124,10 @@ export const dzjk = [
path: 'db', path: 'db',
component: () => import('@/views/ems/dzjk/sbjk/db/index.vue'), component: () => import('@/views/ems/dzjk/sbjk/db/index.vue'),
name: 'DzjkSbjkDb', name: 'DzjkSbjkDb',
hidden: true,
meta: { meta: {
title: '电表', title: '电表',
breadcrumb: false, breadcrumb: false,
activeMenu: '/dzjk/sbjk', activeMenu: '/dzjk',
activeSecondMenuName: 'DzjkSbjk', activeSecondMenuName: 'DzjkSbjk',
deviceCategory: 'AMMETER' deviceCategory: 'AMMETER'
}, },
@ -127,11 +136,10 @@ export const dzjk = [
path: 'yl', path: 'yl',
component: () => import('@/views/ems/dzjk/sbjk/yl/index.vue'), component: () => import('@/views/ems/dzjk/sbjk/yl/index.vue'),
name: 'DzjkSbjkYl', name: 'DzjkSbjkYl',
hidden: true,
meta: { meta: {
title: '冷', title: '冷',
breadcrumb: false, breadcrumb: false,
activeMenu: '/dzjk/sbjk', activeMenu: '/dzjk',
activeSecondMenuName: 'DzjkSbjk', activeSecondMenuName: 'DzjkSbjk',
deviceCategory: 'COOLING' deviceCategory: 'COOLING'
}, },
@ -140,7 +148,6 @@ export const dzjk = [
path: 'dh', path: 'dh',
component: () => import('@/views/ems/dzjk/sbjk/dh/index.vue'), component: () => import('@/views/ems/dzjk/sbjk/dh/index.vue'),
name: 'DzjkSbjkDh', name: 'DzjkSbjkDh',
hidden: true,
meta: { meta: {
title: '动环', title: '动环',
breadcrumb: false, breadcrumb: false,
@ -153,7 +160,6 @@ export const dzjk = [
path: 'xf', path: 'xf',
component: () => import('@/views/ems/dzjk/sbjk/xf/index.vue'), component: () => import('@/views/ems/dzjk/sbjk/xf/index.vue'),
name: 'DzjkSbjkXf', name: 'DzjkSbjkXf',
hidden: true,
meta: { meta: {
title: '消防', title: '消防',
breadcrumb: false, breadcrumb: false,
@ -168,26 +174,28 @@ export const dzjk = [
path: '/dzjk/gzgj', path: '/dzjk/gzgj',
component: () => import('@/views/ems/dzjk/gzgj/index.vue'), component: () => import('@/views/ems/dzjk/gzgj/index.vue'),
name: 'DzjkGzgj', name: 'DzjkGzgj',
meta: {title: '故障告警', breadcrumb: false, activeMenu: '/dzjk/gzgj', activeSecondMenuName: 'DzjkGzgj'} meta: {
title: '故障告警',
breadcrumb: false,
activeMenu: '/dzjk',
activeSecondMenuName: 'DzjkGzgj'
}
}, },
{ {
path: '/dzjk/tjbb', path: '/dzjk/tjbb',
component: () => import('@/views/ems/dzjk/tjbb/index.vue'), component: () => import('@/views/ems/dzjk/tjbb/index.vue'),
name: 'DzjkTjbb', name: 'DzjkTjbb',
alwaysShow: false, meta: {title: '统计报表', breadcrumb: false, activeMenu: '/dzjk'},
meta: {title: '统计报表', breadcrumb: false, activeMenu: '/dzjk/tjbb'},
hidden: false,
redirect: '/dzjk/tjbb/gltj', redirect: '/dzjk/tjbb/gltj',
children: [ children: [
{ {
path: 'gltj', path: 'gltj',
component: () => import('@/views/ems/dzjk/tjbb/gltj/index.vue'), component: () => import('@/views/ems/dzjk/tjbb/gltj/index.vue'),
name: 'DzjkTjbbGltj', name: 'DzjkTjbbGltj',
hidden: true,
meta: { meta: {
title: '运行统计', title: '运行统计',
breadcrumb: false, breadcrumb: false,
activeMenu: '/dzjk/tjbb', activeMenu: '/dzjk',
activeSecondMenuName: 'DzjkTjbb' activeSecondMenuName: 'DzjkTjbb'
}, },
}, },
@ -195,11 +203,10 @@ export const dzjk = [
path: 'glqx', path: 'glqx',
component: () => import('@/views/ems/dzjk/tjbb/glqx/index.vue'), component: () => import('@/views/ems/dzjk/tjbb/glqx/index.vue'),
name: 'DzjkTjbbGlqx', name: 'DzjkTjbbGlqx',
hidden: true,
meta: { meta: {
title: '功率曲线', title: '功率曲线',
breadcrumb: false, breadcrumb: false,
activeMenu: '/dzjk/tjbb', activeMenu: '/dzjk',
activeSecondMenuName: 'DzjkTjbb' activeSecondMenuName: 'DzjkTjbb'
}, },
}, },
@ -207,11 +214,10 @@ export const dzjk = [
path: 'pcsqx', path: 'pcsqx',
component: () => import('@/views/ems/dzjk/tjbb/pcsqx/index.vue'), component: () => import('@/views/ems/dzjk/tjbb/pcsqx/index.vue'),
name: 'DzjkTjbbPcsqx', name: 'DzjkTjbbPcsqx',
hidden: true,
meta: { meta: {
title: 'PCS曲线', title: 'PCS曲线',
breadcrumb: false, breadcrumb: false,
activeMenu: '/dzjk/tjbb', activeMenu: '/dzjk',
activeSecondMenuName: 'DzjkTjbb' activeSecondMenuName: 'DzjkTjbb'
}, },
}, },
@ -219,11 +225,10 @@ export const dzjk = [
path: 'dcdqx', path: 'dcdqx',
component: () => import('@/views/ems/dzjk/tjbb/dcdqx/index.vue'), component: () => import('@/views/ems/dzjk/tjbb/dcdqx/index.vue'),
name: 'DzjkTjbbDcdqx', name: 'DzjkTjbbDcdqx',
hidden: true,
meta: { meta: {
title: '电池堆曲线', title: '电池堆曲线',
breadcrumb: false, breadcrumb: false,
activeMenu: '/dzjk/tjbb', activeMenu: '/dzjk',
activeSecondMenuName: 'DzjkTjbb' activeSecondMenuName: 'DzjkTjbb'
}, },
}, },
@ -231,11 +236,10 @@ export const dzjk = [
path: 'dcwd', path: 'dcwd',
component: () => import('@/views/ems/dzjk/tjbb/dcwd/index.vue'), component: () => import('@/views/ems/dzjk/tjbb/dcwd/index.vue'),
name: 'DzjkTjbbDcwd', name: 'DzjkTjbbDcwd',
hidden: true,
meta: { meta: {
title: '电池温度', title: '电池温度',
breadcrumb: false, breadcrumb: false,
activeMenu: '/dzjk/tjbb', activeMenu: '/dzjk',
activeSecondMenuName: 'DzjkTjbb' activeSecondMenuName: 'DzjkTjbb'
}, },
}, },
@ -243,11 +247,21 @@ export const dzjk = [
path: 'dbbb', path: 'dbbb',
component: () => import('@/views/ems/dzjk/tjbb/dbbb/index.vue'), component: () => import('@/views/ems/dzjk/tjbb/dbbb/index.vue'),
name: 'DzjkTjbbDbbb', name: 'DzjkTjbbDbbb',
hidden: true,
meta: { meta: {
title: '电表报表', title: '电表报表',
breadcrumb: false, breadcrumb: false,
activeMenu: '/dzjk/tjbb', activeMenu: '/dzjk',
activeSecondMenuName: 'DzjkTjbb'
},
},
{
path: 'sybb',
component: () => import('@/views/ems/dzjk/tjbb/sybb/index.vue'),
name: 'DzjkTjbbSybb',
meta: {
title: '收益报表',
breadcrumb: false,
activeMenu: '/dzjk',
activeSecondMenuName: 'DzjkTjbb' activeSecondMenuName: 'DzjkTjbb'
}, },
} }
@ -255,9 +269,33 @@ export const dzjk = [
}, },
{ {
path: '/dzjk/clpz', path: '/dzjk/clpz',
component: () => import('@/views/ems/dzjk/clpz/clyx/index.vue'), component: () => import('@/views/ems/dzjk/clpz/index.vue'),
name: 'DzjkClpz', name: 'DzjkClpz',
meta: {title: '策略配置', breadcrumb: false, activeMenu: '/dzjk/clpz'}, meta: {title: '策略配置', breadcrumb: false, activeMenu: '/dzjk'},
redirect: '/dzjk/clpz/clyx',
children: [
{
path: 'clyx',
component: () => import('@/views/ems/dzjk/clpz/clyx/index.vue'),
name: 'DzjkClpzClyx',
meta: {
title: '策略运行',
breadcrumb: false,
activeMenu: '/dzjk',
activeSecondMenuName: 'DzjkClpz'
},
},
// {
// path: 'xftg',
// component: () => import('@/views/ems/dzjk/clpz/xftg/index.vue'),
// hidden:true,
// breadcrumb: false,
// name: 'DzjkClpzXftg',
// meta: { title: '削峰填谷',breadcrumb: false,activeMenu: '/dzjk',activeSecondMenuName:'DzjkClpz'},
// }
]
}
]
} }
] ]
} }

View File

@ -52,6 +52,11 @@ export const constantRoutes = [
component: () => import('@/views/register'), component: () => import('@/views/register'),
hidden: true hidden: true
}, },
{
path: '/screen',
component: () => import('@/views/screen/index'),
hidden: true
},
{ {
path: '/404', path: '/404',
component: () => import('@/views/error/404'), component: () => import('@/views/error/404'),

View File

@ -7,7 +7,7 @@ module.exports = {
/** /**
* 侧边栏主题 深色主题theme-dark浅色主题theme-light * 侧边栏主题 深色主题theme-dark浅色主题theme-light
*/ */
sideTheme: 'theme-dark', sideTheme: 'theme-light',
/** /**
* 系统布局配置 * 系统布局配置

View File

@ -1,4 +1,3 @@
import {getAllSites} from '@/api/ems/zddt'
import {getAlarmDetailList, getSiteAllDeviceCategory} from '@/api/ems/dzjk' import {getAlarmDetailList, getSiteAllDeviceCategory} from '@/api/ems/dzjk'
const ems = { const ems = {
@ -6,7 +5,17 @@ const ems = {
dzjkAlarmLighting: false,//单站监控 告警统计红点标志 dzjkAlarmLighting: false,//单站监控 告警统计红点标志
zdList: [], zdList: [],
zdDeviceCategoryOptions: {},//站点各个站点包含的设备种类 {021_DDS_01:["BATTERY","CLUSTER","STACK", "DH", "AMMETER", "PCS", "XF"],021_DDS_02:[]...} zdDeviceCategoryOptions: {},//站点各个站点包含的设备种类 {021_DDS_01:["BATTERY","CLUSTER","STACK", "DH", "AMMETER", "PCS", "XF"],021_DDS_02:[]...}
workStatusOptions: {'0': '运行', '1': '机', '2': '故障'},//工作状态 CLUSTERWorkStatusOptions: {'0': '静置', '1': '充电', '2': '放电', '3': '待机', '5': '运行', '9': "故障"},//电池簇工作状态
PCSWorkStatusOptions: {'0': '运行', '1': '停机', '2': '故障', '3': '待机', '4': '充电', '5': '放电'},//PCS工作状态
STACKWorkStatusOptions: {
"0": "静置",
"1": "充电",
"2": "放电",
"3": "浮充",
'4': '待机',
'5': '运行',
'9': "故障"
},//STACKBMS总览工作状态
deviceStatusOptions: {'0': '离线', '1': '在线'},//设备状态 deviceStatusOptions: {'0': '离线', '1': '在线'},//设备状态
gridStatusOptions: {'0': '并网', '1': '未并网'},//并网状态 gridStatusOptions: {'0': '并网', '1': '未并网'},//并网状态
controlModeOptions: {'0': '远程', '1': '本地'},//控制模式 controlModeOptions: {'0': '远程', '1': '本地'},//控制模式
@ -34,14 +43,6 @@ const ems = {
} }
}, },
actions: { actions: {
getZdList({commit, state}) {
if (state.zdList.length === 0) {
getAllSites().then(response => {
commit('SET_ZD_LIST', response?.data || [])
console.log('store action getZdList 获取站点数据', state.zdList)
})
}
},
//查询站点的所有待处理0的告警 存在展示红点标志 //查询站点的所有待处理0的告警 存在展示红点标志
getSiteAlarmNum({state, commit}, siteId) { getSiteAlarmNum({state, commit}, siteId) {
getAlarmDetailList({ getAlarmDetailList({

View File

@ -34,12 +34,11 @@ const permission = {
return new Promise(resolve => { return new Promise(resolve => {
// 向后端请求路由数据 // 向后端请求路由数据
getRouters().then(res => { getRouters().then(res => {
let sysDzjk = -1 let hasDzjk = false
if(res?.data){ if(res?.data){
sysDzjk = res.data.findIndex(i=>{ res.data.forEach(i=>{
return i.children && i.children.find(j=>j.path.indexOf('dzjk')>-1) i.children && i.children.find(j=>j.path.indexOf('dzjk')>-1) && (hasDzjk=true)
}) })
if(sysDzjk>-1) res.data.splice(sysDzjk,1)
} }
const sdata = JSON.parse(JSON.stringify(res.data)) const sdata = JSON.parse(JSON.stringify(res.data))
const rdata = JSON.parse(JSON.stringify(res.data)) const rdata = JSON.parse(JSON.stringify(res.data))
@ -48,7 +47,7 @@ const permission = {
const asyncRoutes = filterDynamicRoutes(dynamicRoutes) const asyncRoutes = filterDynamicRoutes(dynamicRoutes)
rewriteRoutes.push({ path: '*', redirect: '/404', hidden: true }) rewriteRoutes.push({ path: '*', redirect: '/404', hidden: true })
router.addRoutes(asyncRoutes) router.addRoutes(asyncRoutes)
if(sysDzjk === -1){ if(!hasDzjk){
const index = constantRoutes.findIndex(i=>i.path.indexOf('dzjk')>-1) const index = constantRoutes.findIndex(i=>i.path.indexOf('dzjk')>-1)
constantRoutes.splice(index,1) constantRoutes.splice(index,1)
} }

View File

@ -1,6 +1,6 @@
<template> <template>
<div v-loading="loading" class="ems-dashboard-editor-container ems-content-container-padding"> <div v-loading="loading">
<template v-if="!showTemp"> <template v-if="!showTemp">
<el-button v-if="!showTemp" type="primary" plain @click="settingStrategy" style="margin-bottom: 20px;">新增策略</el-button> <el-button v-if="!showTemp" type="primary" plain @click="settingStrategy" style="margin-bottom: 20px;">新增策略</el-button>
<cl-container v-for="(item,index) in list" :key="index+'clContainer'" :info="item" :hide-setting-btn="showTemp" class="contain" @update="init" @showSetting="settingStrategy(item)"> <cl-container v-for="(item,index) in list" :key="index+'clContainer'" :info="item" :hide-setting-btn="showTemp" class="contain" @update="init" @showSetting="settingStrategy(item)">

View File

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

View File

@ -2,7 +2,7 @@
<template> <template>
<!-- <cl-container :hideSettingBtn="true">--> <!-- <cl-container :hideSettingBtn="true">-->
<!-- <template v-slot:default>--> <!-- <template v-slot:default>-->
<div class="ems-dashboard-editor-container ems-content-container-padding"> <div>
<temp-table ref="tempTable" @updateTimeSetting="updateTimeSetting"/> <temp-table ref="tempTable" @updateTimeSetting="updateTimeSetting"/>
<time-setting ref="timeSetting"/> <time-setting ref="timeSetting"/>
<temp-power-chart ref="tomePowerChart"/> <temp-power-chart ref="tomePowerChart"/>

View File

@ -1,6 +1,6 @@
<template> <template>
<div v-loading="loading" class="ems-dashboard-editor-container ems-content-container-padding"> <el-card v-loading="loading" gshadow="always" class="common-card-container common-card-container-no-title-bg">
<!-- 搜索栏--> <!-- 搜索栏-->
<el-form :inline="true" class="select-container"> <el-form :inline="true" class="select-container">
<el-form-item label="设备清单"> <el-form-item label="设备清单">
@ -108,7 +108,7 @@
> >
</el-pagination> </el-pagination>
</div> </div>
</div> </el-card>
</template> </template>

View File

@ -1,9 +1,8 @@
<template> <template>
<el-card shadow="always" class="common-card-container common-card-container-body-no-padding time-range-card"> <el-card shadow="always" class="common-card-container common-card-container-body-no-padding time-range-card">
<div slot="header" class="time-range-header"> <div slot="header" class="time-range-header">
<span class="card-title">当日功率曲线</span> <span class="card-title">当日功率曲线</span>
<date-range-select ref="dateRangeSelect" @updateDate="updateDate"/> <date-range-select ref="dateRangeSelect" :showIcon="true" :mini-time-picker="true" @updateDate="updateDate"/>
</div> </div>
<div style="height: 310px" id="activeChart"></div> <div style="height: 310px" id="activeChart"></div>
</el-card> </el-card>
@ -13,18 +12,18 @@
import * as echarts from 'echarts' import * as echarts from 'echarts'
import resize from '@/mixins/ems/resize' import resize from '@/mixins/ems/resize'
import DateRangeSelect from '@/components/Ems/DateRangeSelect/index.vue' import DateRangeSelect from '@/components/Ems/DateRangeSelect/index.vue'
import { getPointData } from '@/api/ems/dzjk' import {getPointData} from '@/api/ems/dzjk'
import intervalUpdate from "@/mixins/ems/intervalUpdate"; import intervalUpdate from "@/mixins/ems/intervalUpdate";
export default { export default {
mixins: [resize,intervalUpdate], mixins: [resize, intervalUpdate],
components: {DateRangeSelect}, components: {DateRangeSelect},
data() { data() {
return { return {
chart: null, chart: null,
timeRange:[], timeRange: [],
siteId:'', siteId: '',
isInit:true isInit: true
} }
}, },
mounted() { mounted() {
@ -41,23 +40,23 @@ export default {
}, },
methods: { methods: {
// 更新时间范围 重置图表 // 更新时间范围 重置图表
updateDate(data){ updateDate(data) {
this.timeRange=data this.timeRange = data
!this.isInit && this.getGVQXData() !this.isInit && this.getGVQXData()
this.isInit = false this.isInit = false
}, },
getGVQXData(){ getGVQXData() {
this.showLoading() this.showLoading()
const {siteId,timeRange}=this const {siteId, timeRange} = this
getPointData({siteId,startDate:timeRange[0],endDate:timeRange[1]}).then(response => { getPointData({siteId, startDate: timeRange[0], endDate: timeRange[1]}).then(response => {
this.setOption(response?.data || []) this.setOption(response?.data || [])
}).finally(()=>this.hideLoading()) }).finally(() => this.hideLoading())
}, },
init(siteId){ init(siteId) {
//初始化 清空数据 //初始化 清空数据
this.siteId = siteId this.siteId = siteId
this.isInit = true this.isInit = true
this.timeRange=[] this.timeRange = []
this.$refs.dateRangeSelect.init(true) this.$refs.dateRangeSelect.init(true)
this.getGVQXData() this.getGVQXData()
this.updateInterval(this.getGVQXData) this.updateInterval(this.getGVQXData)
@ -65,17 +64,17 @@ export default {
initChart() { initChart() {
this.chart = echarts.init(document.querySelector('#activeChart')) this.chart = echarts.init(document.querySelector('#activeChart'))
}, },
showLoading(){ showLoading() {
this.chart && this.chart.showLoading() this.chart && this.chart.showLoading()
}, },
hideLoading(){ hideLoading() {
this.chart && this.chart.hideLoading() this.chart && this.chart.hideLoading()
}, },
setOption(data) { setOption(data) {
const source = [['日期','电网功率','负载功率','储能功率','光伏功率','soc平均值','soh平均值','电池平均温度平均值']] const source = [['日期', '电网功率', '负载功率', '储能功率', '光伏功率', 'soc平均值', 'soh平均值', '电池平均温度平均值']]
console.log('source.slice(1)',source[0].slice(1)) console.log('source.slice(1)', source[0].slice(1))
this.chart && data.forEach((item)=>{ this.chart && data.forEach((item) => {
source.push([item.statisDate,item.gridPower,item.loadPower,item.storagePower,item.pvPower,item.avgSoc,item.avgSoh,item.avgTemp]) source.push([item.statisDate, item.gridPower, item.loadPower, item.storagePower, item.pvPower, item.avgSoc, item.avgSoh, item.avgTemp])
}) })
this.chart.setOption({ this.chart.setOption({
grid: { grid: {
@ -91,8 +90,8 @@ export default {
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow' type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
} }
}, },
textStyle:{ textStyle: {
color:"#333333", color: "#333333",
}, },
xAxis: { xAxis: {
type: 'category', type: 'category',
@ -105,11 +104,17 @@ export default {
type: 'value', type: 'value',
}, },
], ],
dataset:{source}, dataset: {source},
series: source[0].slice(1).map((item,index)=>{ series: source[0].slice(1).map((item, index) => {
return { return {
type: 'line', type: 'line',//index === 5 ? 'bar' : 'line',
yAxisIndex:index<=4 ? 0 : 1 showSymbol: false,
symbolSize: 2,
smooth: true,
areaStyle: {
opacity: 0.5,
},
yAxisIndex: index <= 4 ? 0 : 1
} }
}) })
}) })

View File

@ -1,9 +1,8 @@
<template> <template>
<el-card shadow="always" class="common-card-container common-card-container-body-no-padding time-range-card"> <el-card shadow="always" class="common-card-container common-card-container-body-no-padding time-range-card">
<div slot="header" class="time-range-header"> <div slot="header" class="time-range-header">
<span class="card-title">一周充放曲线</span> <span class="card-title">一周充放曲线</span>
<date-range-select ref="dateRangeSelect" @updateDate="updateDate"/> <date-range-select ref="dateRangeSelect" :showIcon="true" :mini-time-picker="true" @updateDate="updateDate"/>
</div> </div>
<div style="height: 310px" id="weekChart"></div> <div style="height: 310px" id="weekChart"></div>
</el-card> </el-card>
@ -14,14 +13,15 @@ import * as echarts from 'echarts'
import resize from '@/mixins/ems/resize' import resize from '@/mixins/ems/resize'
import DateRangeSelect from '@/components/Ems/DateRangeSelect/index.vue' import DateRangeSelect from '@/components/Ems/DateRangeSelect/index.vue'
import {getSevenChargeData} from '@/api/ems/dzjk' import {getSevenChargeData} from '@/api/ems/dzjk'
export default { export default {
mixins: [resize], mixins: [resize],
components: {DateRangeSelect}, components: {DateRangeSelect},
data() { data() {
return { return {
chart: null, chart: null,
timeRange:[], timeRange: [],
siteId:'', siteId: '',
} }
}, },
mounted() { mounted() {
@ -38,41 +38,40 @@ export default {
}, },
methods: { methods: {
// 更新时间范围 重置图表 // 更新时间范围 重置图表
updateDate(data){ updateDate(data) {
this.timeRange=data this.timeRange = data
this.getWeekKData() this.getWeekKData()
}, },
getWeekKData(){ getWeekKData() {
this.showLoading() this.showLoading()
const {siteId,timeRange}=this const {siteId, timeRange} = this
this.hideLoading() getSevenChargeData({siteId, startDate: timeRange[0], endDate: timeRange[1]}).then(response => {
getSevenChargeData({siteId,startDate:timeRange[0],endDate:timeRange[1]}).then(response => {
this.setOption(response?.data || []) this.setOption(response?.data || [])
}).finally(()=>this.hideLoading()) }).finally(() => this.hideLoading())
}, },
init(siteId){ init(siteId) {
//初始化 清空数据 //初始化 清空数据
this.siteId = siteId this.siteId = siteId
this.timeRange=[] this.timeRange = []
this.deviceId='' this.deviceId = ''
this.$refs.dateRangeSelect.init() this.$refs.dateRangeSelect.init()
}, },
initChart() { initChart() {
this.chart = echarts.init(document.querySelector('#weekChart')) this.chart = echarts.init(document.querySelector('#weekChart'))
}, },
showLoading(){ showLoading() {
this.chart && this.chart.showLoading() this.chart && this.chart.showLoading()
}, },
hideLoading(){ hideLoading() {
this.chart && this.chart.hideLoading() this.chart && this.chart.hideLoading()
}, },
setOption(data,unit) { setOption(data, unit) {
const source = [['日期','充电量','放电量']] const source = [['日期', '充电量', '放电量']]
data.forEach(item=>{ data.forEach(item => {
source.push([item.ammeterDate, item.chargedCap,item.disChargedCap]) source.push([item.ammeterDate, item.chargedCap, item.disChargedCap])
}) })
this.chart && this.chart.setOption({ this.chart && this.chart.setOption({
color:['#4472c4','#70ad47'],//所有充放电颜色保持统一 color: ['#4472c4', '#70ad47'],//所有充放电颜色保持统一
tooltip: { tooltip: {
trigger: 'axis', trigger: 'axis',
axisPointer: { // 坐标轴指示器,坐标轴触发有效 axisPointer: { // 坐标轴指示器,坐标轴触发有效
@ -88,29 +87,29 @@ export default {
}, },
xAxis: { xAxis: {
type: 'category', type: 'category',
name:unit, name: unit,
nameLocation:'center' nameLocation: 'center'
}, },
yAxis: [{ yAxis: [{
type: 'value', type: 'value',
name:'充电量/放电量kWh', name: '充电量/放电量kWh',
axisLine: { axisLine: {
lineStyle:{ lineStyle: {
color: '#333333', color: '#333333',
}, },
onZero:false onZero: false
} }
}], }],
dataset:{ dataset: {
source source
}, },
series: [ series: [
{ {
yAxisIndex:0, yAxisIndex: 0,
type: 'bar', type: 'bar',
}, },
{ {
yAxisIndex:0, yAxisIndex: 0,
type: 'bar', type: 'bar',
}, },
] ]

View File

@ -1,10 +1,8 @@
<template> <template>
<div <div v-loading="loading">
class="ems-dashboard-editor-container ems-content-container-padding"
v-loading="loading"
>
<el-row style="background: #fff" class="row-container" :gutter="15"> <el-row style="background: #fff" class="row-container" :gutter="15">
<el-col :xs="24" :sm="24" :lg="8"> <el-col :xs="24" :sm="24" :lg="5">
<!-- 站点信息-->
<el-card <el-card
shadow="always" shadow="always"
class="common-card-container common-card-container-body-no-padding" class="common-card-container common-card-container-body-no-padding"
@ -12,87 +10,92 @@
<div slot="header"> <div slot="header">
<span class="card-title">站点信息</span> <span class="card-title">站点信息</span>
<div class="alarm-msg" v-if="tableData.length > 0" @click="toAlarm"> <div class="alarm-msg" v-if="tableData.length > 0" @click="toAlarm">
<i class="el-icon-message-solid"></i> <i class="el-icon-message-solid"></i> 设备告警
</div> </div>
</div> </div>
<div <div
style="box-sizing: border-box; height: 250px; padding: 20px 15px" style="box-sizing: border-box; height: 218px; padding: 20px 15px"
> >
<el-descriptions class="home-normal-info" :column="1"> <!-- 地址运行时间-->
<el-descriptions-item <div class="site-info site-info-address">
size="mini" <div class="title">
v-for="(item, index) in singleZdInfo" <i class="el-icon-location"></i>
:key="index + 'singleZdInfo'" </div>
:label="item.title" <div class="value">{{ info.siteAddress }}</div>
>{{ info[item.attr] | formatNumber }} </div>
</el-descriptions-item <div class="site-info">
<div class="title">
<i class="el-icon-date"></i>
</div>
<div class="value">{{ info.runningTime || '-' }}</div>
</div>
<!-- 装机功率容量 -->
<el-row :gutter="10" style="margin-top:20px;">
<el-col
:span="12"
class="sjgl-col power-col"
> >
</el-descriptions> <div class="sjgl-wrapper">
<div class="sjgl-title">装机功率(MW)</div>
<div class="sjgl-value">
{{ info.installPower | formatNumber }}
</div>
</div>
</el-col>
<el-col
:span="12"
class="sjgl-col power-col"
>
<div class="sjgl-wrapper">
<div class="sjgl-title">装机容量(MW)</div>
<div class="sjgl-value">
{{ info.installCapacity | formatNumber }}
</div>
</div>
</el-col>
</el-row>
</div> </div>
</el-card> </el-card>
</el-col> </el-col>
<el-col :xs="24" :sm="24" :lg="16"> <!-- 总累计运行数据-->
<cl-info :info="runningInfo.strategyTempInfo"/> <el-col :xs="24" :sm="24" :lg="19">
</el-col>
<el-col :xs="24" :sm="24" :lg="12">
<el-card <el-card
shadow="always" shadow="always"
class="common-card-container common-card-container-body-no-padding" 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 class="total-count">
<span class="title">总收入</span>
<span class="value">{{ runningInfo.totalRevenue | formatNumber }}</span>
<span class="unit"></span>
</div>
</div> </div>
<div <div
style="box-sizing: border-box; height: 250px; padding: 20px 15px" style="box-sizing: border-box; height: 218px; padding: 20px 15px"
> >
<el-row :gutter="20"> <el-row :gutter="10">
<el-col <el-col
:span="12" :span="6"
v-for="(item, index) in sjglData" v-for="(item, index) in sjglData"
:key="index + 'sjglData'" :key="index + 'sjglData'"
class="sjgl-data" class="sjgl-col"
> >
<div class="sjgl-wrapper">
<div class="sjgl-title">{{ item.title }}</div> <div class="sjgl-title">{{ item.title }}</div>
<div class="sjgl-value"> <div class="sjgl-value" :style="{color:item.color}">
{{ runningInfo[item.attr] | formatNumber }} {{ runningInfo[item.attr] | formatNumber }}
</div> </div>
</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="12"> <el-col :xs="24" :sm="24" :lg="12">
<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-col
v-for="(item, index) in revenueData"
:key="index + 'revenueData'"
:span="index === 2 ? 24 : 12"
class="sjgl-data"
:style="{marginTop:index === 2 ? '40px' : 0}"
>
<div class="sjgl-title">{{ item.title }}</div>
<div class="sjgl-value">
{{ runningInfo[item.attr] | formatNumber }}
</div>
</el-col>
</el-row>
</div>
</el-card>
</el-col>
<el-col :xs="24" :sm="24" :lg="24">
<week-chart ref="weekChart"/> <week-chart ref="weekChart"/>
</el-col> </el-col>
<el-col :xs="24" :sm="24" :lg="24"> <el-col :xs="24" :sm="24" :lg="12">
<active-chart ref="activeChart"/> <active-chart ref="activeChart"/>
</el-col> </el-col>
</el-row> </el-row>
@ -116,62 +119,46 @@ export default {
data() { data() {
return { return {
loading: false, loading: false,
singleZdInfo: [
{
title: "电站位置",
attr: "siteAddress",
},
{
title: "投运时间",
attr: "runningTime",
},
{
title: "装机功率(MW)",
attr: "installPower",
},
{
title: "装机容量(MW)",
attr: "installCapacity",
},
],
sjglData: [ sjglData: [
{ {
title: "今日充电量kWh", title: "今日充电量kWh",
attr: "dayChargedCap", attr: "dayChargedCap",
color: '#4472c4'
}, },
{ {
title: "今日放电量kWh", title: "今日放电量kWh",
attr: "dayDisChargedCap", attr: "dayDisChargedCap",
}, color: '#70ad47'
{
title: "昨日充电量kWh",
attr: "todo",
},
{
title: "昨日放电量kWh",
attr: "todo",
}, },
{ {
title: "总充电量kWh", title: "总充电量kWh",
attr: "totalChargedCap", attr: "totalChargedCap",
color: '#4472c4'
},
{
title: "今日实时收入(元)",
attr: "dayRevenue",
color: '#f67438'
},
{
title: "昨日充电量kWh",
attr: "yesterdayChargedCap",
color: '#4472c4'
},
{
title: "昨日放电量kWh",
attr: "yesterdayDisChargedCap",
color: '#70ad47'
}, },
{ {
title: "总放电量kWh", title: "总放电量kWh",
attr: "totalDischargedCap", attr: "totalDischargedCap",
}, color: '#70ad47'
],
revenueData: [
{
title: "当日实时收入(元)",
attr: "dayRevenue",
}, },
{ {
title: "昨日实时收入(元)", title: "昨日实时收入(元)",
attr: "todo", attr: "yesterdayRevenue",
}, color: '#f67438'
{
title: "总收入(元)",
attr: "totalRevenue",
}, },
], ],
info: {}, //基本信息 info: {}, //基本信息
@ -180,10 +167,6 @@ export default {
}, },
computed: { computed: {
tableData() { tableData() {
console.log(
"this.runningInfo?.siteMonitorHomeAlarmVo ",
this.runningInfo?.siteMonitorHomeAlarmVo
);
return this.runningInfo?.siteMonitorHomeAlarmVo || []; return this.runningInfo?.siteMonitorHomeAlarmVo || [];
}, },
}, },
@ -220,21 +203,73 @@ export default {
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
//设备告警
.alarm-msg { .alarm-msg {
background: #ffcec6; background: #ff4949;
width: 32px; padding: 2px 5px;
border-radius: 17px; font-size: 10px;
text-align: center; font-weight: bolder;
font-size: 22px; border-radius: 3px;
line-height: 30px; line-height: 20px;
cursor: pointer; cursor: pointer;
color: #ff4949; color: #fff;
position: absolute; position: absolute;
right: 20px; right: 10px;
top: 50%; top: 50%;
transform: translateY(-50%); transform: translateY(-50%);
} }
//基本信息-地址 运行️时间
.site-info {
display: flex;
font-size: 12px;
line-height: 20px;
margin-bottom: 10px;
align-items: center;
&.site-info-address {
height: 40px;
}
.title {
color: #1791ff;
font-size: 18px;
line-height: 20px;
margin-right: 7px;
}
.value {
color: #000;
font-size: 12px;
max-height: 40px;
overflow: hidden;
}
}
//总收入
.total-count {
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
font-size: 12px;
font-weight: bolder;
color: #333;
line-height: 14px;
.unit {
font-style: italic;
}
.value {
font-size: 22px;
font-weight: bolder;
color: #ed2f1d;
font-style: italic;
padding: 0 5px;
}
}
.row-container { .row-container {
& > .el-col { & > .el-col {
margin-bottom: 20px; margin-bottom: 20px;
@ -242,28 +277,47 @@ export default {
} }
//数据概览 //数据概览
.sjgl-data { .sjgl-col {
text-align: center; .sjgl-wrapper {
text-align: left;
padding: 15px 20px;
background-color: #f2f7fb;
}
&:nth-child(1), &.power-col {
.sjgl-wrapper {
padding: 10px;
.sjgl-value {
color: #c44444;
}
}
}
&:nth-child(4),
&:nth-child(2), &:nth-child(2),
&:nth-child(3), &:nth-child(3),
&:nth-child(4) { &:nth-child(4) {
margin-bottom: 25px; margin-bottom: 10px;
} }
.sjgl-title { .sjgl-title {
color: #666666; color: #717171;
line-height: 14px; line-height: 14px;
font-weight: bold;
} }
.sjgl-value { .sjgl-value {
color: rgba(51, 51, 51, 1); color: rgba(51, 51, 51, 1);
font-size: 26px; font-size: 22px;
line-height: 26px; line-height: 26px;
font-weight: 500; font-weight: bolder;
font-style: italic;
margin-top: 14px; margin-top: 14px;
word-wrap: break-word; width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
} }
} }
</style> </style>

View File

@ -3,11 +3,7 @@
<div v-for="(baseInfo,index) in baseInfoList" :key="index+'bmsdccContainer'" style="margin-bottom:25px;"> <div v-for="(baseInfo,index) in baseInfoList" :key="index+'bmsdccContainer'" style="margin-bottom:25px;">
<el-card shadow="always" <el-card shadow="always"
class="sbjk-card-container common-card-container-body-no-padding common-card-container-no-title-bg" class="sbjk-card-container common-card-container-body-no-padding common-card-container-no-title-bg"
:class="{ :class="handleCardClass(baseInfo)">
'timing-card-container':!['0','2'].includes(baseInfo.workStatus),
'warning-card-container':baseInfo.workStatus === '2',
'running-card-container':baseInfo.workStatus === '0'
}">
<div slot="header"> <div slot="header">
<span <span
class="large-title">{{ class="large-title">{{
@ -20,7 +16,7 @@
<el-button type="primary" round size="small" style="margin-right:20px;" <el-button type="primary" round size="small" style="margin-right:20px;"
@click="pointDetail(baseInfo,'point')">详细 @click="pointDetail(baseInfo,'point')">详细
</el-button> </el-button>
<el-badge :value="baseInfo.alarmNum || 0" class="item"> <el-badge :hidden="!baseInfo.alarmNum" :value="baseInfo.alarmNum || 0" class="item">
<i <i
class="el-icon-message-solid alarm-icon" class="el-icon-message-solid alarm-icon"
@click="pointDetail(baseInfo,'alarmPoint')" @click="pointDetail(baseInfo,'alarmPoint')"
@ -30,16 +26,16 @@
</div> </div>
<div class="descriptions-main"> <div class="descriptions-main">
<el-descriptions direction="vertical" :column="3" :colon="false"> <el-descriptions direction="vertical" :column="3" :colon="false">
<el-descriptions-item labelClassName="descriptions-label" <el-descriptions-item
:contentClassName="`descriptions-direction ${baseInfo.workStatus === '0' ? 'save' :'danger'}`" contentClassName="descriptions-direction work-status"
:span="1" label="工作状态"> :span="1" label="工作状态">
{{ $store.state.ems.workStatusOptions[baseInfo.workStatus] }} {{ CLUSTERWorkStatusOptions[baseInfo.workStatus] }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item labelClassName="descriptions-label" contentClassName="descriptions-direction" <el-descriptions-item contentClassName="descriptions-direction"
:span="1" label="与PCS通信"> :span="1" label="与PCS通信">
{{ $store.state.ems.communicationStatusOptions[baseInfo.pcsCommunicationStatus] }} {{ $store.state.ems.communicationStatusOptions[baseInfo.pcsCommunicationStatus] }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item labelClassName="descriptions-label" contentClassName="descriptions-direction" <el-descriptions-item contentClassName="descriptions-direction"
:span="1" label="与EMS通信"> :span="1" label="与EMS通信">
{{ $store.state.ems.communicationStatusOptions[baseInfo.emsCommunicationStatus] }} {{ $store.state.ems.communicationStatusOptions[baseInfo.emsCommunicationStatus] }}
</el-descriptions-item> </el-descriptions-item>
@ -132,11 +128,17 @@ import PointTable from "@/views/ems/site/sblb/PointTable.vue";
import {getBMSBatteryCluster} from '@/api/ems/dzjk' import {getBMSBatteryCluster} from '@/api/ems/dzjk'
import getQuerySiteId from "@/mixins/ems/getQuerySiteId"; import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
import intervalUpdate from "@/mixins/ems/intervalUpdate"; import intervalUpdate from "@/mixins/ems/intervalUpdate";
import {mapState} from "vuex";
export default { export default {
name: 'DzjkSbjkBmsdcc', name: 'DzjkSbjkBmsdcc',
mixins: [getQuerySiteId, intervalUpdate], mixins: [getQuerySiteId, intervalUpdate],
components: {PointTable, pointChart}, components: {PointTable, pointChart},
computed: {
...mapState({
CLUSTERWorkStatusOptions: state => state?.ems?.CLUSTERWorkStatusOptions || {},
})
},
data() { data() {
return { return {
loading: false, loading: false,
@ -171,6 +173,10 @@ export default {
} }
}, },
methods: { methods: {
handleCardClass(item) {
const {workStatus = ''} = item
return !(Object.keys(this.CLUSTERWorkStatusOptions).includes(item.workStatus)) ? "timing-card-container" : workStatus === '9' ? 'warning-card-container' : 'running-card-container'
},
// 查看设备电位表格 // 查看设备电位表格
pointDetail(row, dataType) { pointDetail(row, dataType) {
const {siteId, deviceId} = row const {siteId, deviceId} = row

View File

@ -1,11 +1,9 @@
<template> <template>
<div v-loading="loading"> <div v-loading="loading">
<div v-for="(baseInfo,index) in baseInfoList" :key="index+'bmszlContainer'" style="margin-bottom:25px;"> <div v-for="(baseInfo,index) in baseInfoList" :key="index+'bmszlContainer'" style="margin-bottom:25px;">
<el-card :class="{ <el-card
'timing-card-container':!['0','2'].includes(baseInfo.workStatus), :class="handleCardClass(baseInfo)"
'warning-card-container':baseInfo.workStatus === '2', class="sbjk-card-container common-card-container-body-no-padding common-card-container-no-title-bg"
'running-card-container':baseInfo.workStatus === '0'
}" class="sbjk-card-container common-card-container-body-no-padding common-card-container-no-title-bg"
shadow="always"> shadow="always">
<div slot="header"> <div slot="header">
<span class="large-title">{{ baseInfo.deviceName }}</span> <span class="large-title">{{ baseInfo.deviceName }}</span>
@ -16,7 +14,7 @@
<el-button type="primary" round size="small" style="margin-right:20px;" <el-button type="primary" round size="small" style="margin-right:20px;"
@click="pointDetail(baseInfo,'point')">详细 @click="pointDetail(baseInfo,'point')">详细
</el-button> </el-button>
<el-badge :value="baseInfo.alarmNum || 0" class="item"> <el-badge :hidden="!baseInfo.alarmNum" :value="baseInfo.alarmNum || 0" class="item">
<i <i
class="el-icon-message-solid alarm-icon" class="el-icon-message-solid alarm-icon"
@click="pointDetail(baseInfo,'alarmPoint')" @click="pointDetail(baseInfo,'alarmPoint')"
@ -27,9 +25,9 @@
<div class="descriptions-main"> <div class="descriptions-main">
<el-descriptions :colon="false" :column="3" direction="vertical"> <el-descriptions :colon="false" :column="3" direction="vertical">
<el-descriptions-item <el-descriptions-item
:contentClassName="`descriptions-direction ${baseInfo.workStatus === '0' ? 'save' :'danger'}`" :span="1" contentClassName="descriptions-direction work-status"
label="工作状态" labelClassName="descriptions-label"> label="工作状态" labelClassName="descriptions-label">
{{ $store.state.ems.workStatusOptions[baseInfo.workStatus] }} {{ STACKWorkStatusOptions[baseInfo.workStatus] }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item :span="1" contentClassName="descriptions-direction" label="与PCS通信" <el-descriptions-item :span="1" contentClassName="descriptions-direction" label="与PCS通信"
labelClassName="descriptions-label"> labelClassName="descriptions-label">
@ -164,11 +162,17 @@ import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
import intervalUpdate from "@/mixins/ems/intervalUpdate"; import intervalUpdate from "@/mixins/ems/intervalUpdate";
import pointChart from "./../PointChart.vue"; import pointChart from "./../PointChart.vue";
import PointTable from "@/views/ems/site/sblb/PointTable.vue"; import PointTable from "@/views/ems/site/sblb/PointTable.vue";
import {mapState} from "vuex";
export default { export default {
name: 'DzjkSbjkBmszl', name: 'DzjkSbjkBmszl',
components: {pointChart, PointTable}, components: {pointChart, PointTable},
mixins: [getQuerySiteId, intervalUpdate], mixins: [getQuerySiteId, intervalUpdate],
computed: {
...mapState({
STACKWorkStatusOptions: state => state?.ems?.STACKWorkStatusOptions || {},
})
},
data() { data() {
return { return {
loading: false, loading: false,
@ -187,6 +191,11 @@ export default {
} }
}, },
methods: { methods: {
handleCardClass(item) {
const {workStatus = ''} = item
return !Object.keys(this.STACKWorkStatusOptions).find(i => i === workStatus) ? "timing-card-container" : workStatus === '9' ? 'warning-card-container' : 'running-card-container'
},
// 查看设备电位表格 // 查看设备电位表格
pointDetail(row, dataType) { pointDetail(row, dataType) {
const {siteId, deviceId} = row const {siteId, deviceId} = row

View File

@ -6,7 +6,8 @@
shadow="always" shadow="always"
class="sbjk-card-container list" class="sbjk-card-container list"
:class="{ :class="{
'warning-card-container':item.emsCommunicationStatus && item.emsCommunicationStatus !== '0', 'timing-card-container':!['0','2'].includes(item.emsCommunicationStatus),
'warning-card-container':item.emsCommunicationStatus === '2',
'running-card-container':item.emsCommunicationStatus === '0' 'running-card-container':item.emsCommunicationStatus === '0'
}" }"
> >
@ -15,18 +16,16 @@
<div class="info"> <div class="info">
<div> <div>
{{ {{
$store.state.ems.communicationStatusOptions[ communicationStatusOptions[item.emsCommunicationStatus] || '-'
item.emsCommunicationStatus
]
}} }}
</div> </div>
<div>数据更新时间{{ item.dataUpdateTime }}</div> <div>数据更新时间{{ item.dataUpdateTime || '-' }}</div>
</div> </div>
<div class="alarm"> <div class="alarm">
<el-button type="primary" round size="small" style="margin-right:20px;" @click="pointDetail(item,'point')"> <el-button type="primary" round size="small" style="margin-right:20px;" @click="pointDetail(item,'point')">
详细 详细
</el-button> </el-button>
<el-badge :value="item.alarmNum || 0" class="item"> <el-badge :hidden="!item.alarmNum" :value="item.alarmNum || 0" class="item">
<i <i
class="el-icon-message-solid alarm-icon" class="el-icon-message-solid alarm-icon"
@click="pointDetail(item,'alarmPoint')" @click="pointDetail(item,'alarmPoint')"
@ -35,7 +34,8 @@
</div> </div>
</div> </div>
<el-row class="device-info-row"> <el-row class="device-info-row">
<el-col v-for="(tempDataItem,tempDataIndex) in deviceIdTypeMsg[item.deviceId]" :key="tempDataIndex+'dbTempData'" <el-col v-for="(tempDataItem,tempDataIndex) in (deviceIdTypeMsg[item.deviceId] || otherTypeMsg)"
:key="tempDataIndex+'dbTempData'"
:span="8" class="device-info-col"> :span="8" class="device-info-col">
<span class="pointer" @click="showChart(tempDataItem.pointName,item.deviceId)"> <span class="pointer" @click="showChart(tempDataItem.pointName,item.deviceId)">
<span class="left">{{ tempDataItem.name }}</span> <span class="right">{{ item[tempDataItem.attr] || '-' }}<span <span class="left">{{ tempDataItem.name }}</span> <span class="right">{{ item[tempDataItem.attr] || '-' }}<span
@ -56,11 +56,18 @@ import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
import {getAmmeterDataList} from "@/api/ems/dzjk"; import {getAmmeterDataList} from "@/api/ems/dzjk";
import intervalUpdate from "@/mixins/ems/intervalUpdate"; import intervalUpdate from "@/mixins/ems/intervalUpdate";
import PointTable from "@/views/ems/site/sblb/PointTable.vue"; import PointTable from "@/views/ems/site/sblb/PointTable.vue";
import {mapState} from "vuex";
export default { export default {
name: "DzjkSbjkDb", name: "DzjkSbjkDb",
mixins: [getQuerySiteId, intervalUpdate], mixins: [getQuerySiteId, intervalUpdate],
components: {PointTable, pointChart}, components: {PointTable, pointChart},
computed: {
...mapState({
communicationStatusOptions: state => state?.ems?.communicationStatusOptions || {},
})
},
data() { data() {
return { return {
loading: false, loading: false,
@ -70,89 +77,125 @@ export default {
{ {
name: '正向有功电能', name: '正向有功电能',
attr: 'forwardActive', attr: 'forwardActive',
pointName: '正向有功电能' pointName: '正向有功电能',
unit: 'kWh'
}, },
{ {
name: '反向有功电能', name: '反向有功电能',
attr: 'reverseActive', attr: 'reverseActive',
pointName: '反向有功电能' pointName: '反向有功电能',
unit: 'kWh'
}, },
{ {
name: '正向无功电能', name: '正向无功电能',
attr: 'forwardReactive', attr: 'forwardReactive',
pointName: '正向无功电能' pointName: '正向无功电能',
unit: 'kvarh'
}, },
{ {
name: '反向无功电能', name: '反向无功电能',
attr: 'reverseReactive', attr: 'reverseReactive',
pointName: '反向无功电能' pointName: '反向无功电能',
unit: 'kvarh'
}, },
{ {
name: '有功功率', name: '有功功率',
attr: 'activePower', attr: 'activePower',
pointName: '总有功功率' pointName: '总有功功率',
unit: 'kW'
}, },
{ {
name: '无功功率', name: '无功功率',
attr: 'reactivePower', attr: 'reactivePower',
pointName: '总无功功率' pointName: '总无功功率',
unit: 'kvar'
} }
], ],
'METE': [ 'METE': [
{ {
name: '正向有功电能', name: '正向有功电能',
attr: 'forwardActive', attr: 'forwardActive',
pointName: '正向有功电能' pointName: '正向有功电能',
unit: 'kWh'
}, },
{ {
name: '反向有功电能', name: '反向有功电能',
attr: 'reverseActive', attr: 'reverseActive',
pointName: '反向有功电能' pointName: '反向有功电能',
unit: 'kWh'
}, },
{ {
name: '正向无功电能', name: '正向无功电能',
attr: 'forwardReactive', attr: 'forwardReactive',
pointName: '正向无功电能' pointName: '正向无功电能',
unit: 'kvarh'
}, },
{ {
name: '反向无功电能', name: '反向无功电能',
attr: 'reverseReactive', attr: 'reverseReactive',
pointName: '反向无功电能' pointName: '反向无功电能',
unit: 'kvarh'
}, },
{ {
name: '有功功率', name: '有功功率',
attr: 'activePower', attr: 'activePower',
pointName: '总有功功率' pointName: '总有功功率',
unit: 'kW'
}, },
{ {
name: '无功功率', name: '无功功率',
attr: 'reactivePower', attr: 'reactivePower',
pointName: '总无功功率' pointName: '总无功功率',
unit: 'kvar'
} }
], ],
'METEGF': [ 'METEGF': [
{ {
name: '有功电能', name: '有功电能',
attr: 'activeEnergy', attr: 'activeEnergy',
pointName: '有功电能' pointName: '有功电能',
unit: 'kWh'
}, },
{ {
name: '无功电能', name: '无功电能',
attr: 'reactiveEnergy', attr: 'reactiveEnergy',
pointName: '无功电能' pointName: '无功电能',
unit: 'kvarh'
}, },
{ {
name: '有功功率', name: '有功功率',
attr: 'activePower', attr: 'activePower',
pointName: '总有功功率' pointName: '总有功功率',
unit: 'kW'
}, },
{ {
name: '无功功率', name: '无功功率',
attr: 'reactivePower', attr: 'reactivePower',
pointName: '总无功功率' pointName: '总无功功率',
unit: 'kvar'
} }
] ]
} },
otherTypeMsg: [
{
name: '正向有功电能',
attr: 'forwardActive',
pointName: '正向有功电能',
unit: 'kWh'
},
{
name: '反向有功电能',
attr: 'reverseActive',
pointName: '反向有功电能',
unit: 'kWh'
},
{
name: '有功功率',
attr: 'activePower',
pointName: '总有功功率',
unit: 'kW'
},
]
}; };
}, },
methods: { methods: {

View File

@ -14,7 +14,7 @@
<el-button type="primary" round size="small" style="margin-right:20px;" @click="pointDetail(item,'point')"> <el-button type="primary" round size="small" style="margin-right:20px;" @click="pointDetail(item,'point')">
详细 详细
</el-button> </el-button>
<el-badge :value="item.alarmNum || 0" class="item"> <el-badge :hidden="!item.alarmNum" :value="item.alarmNum || 0" class="item">
<i <i
class="el-icon-message-solid alarm-icon" class="el-icon-message-solid alarm-icon"
@click="pointDetail(item,'alarmPoint')" @click="pointDetail(item,'alarmPoint')"

View File

@ -1,206 +1,5 @@
<template> <template>
<div v-loading="loading" class="ems"> <div v-loading="loading" class="ems">
<!-- <div-->
<!-- v-for="(item, index) in list"-->
<!-- :key="index + 'PcsHome'"-->
<!-- style="margin-bottom: 25px"-->
<!-- >-->
<!-- <el-card-->
<!-- :class="{-->
<!-- 'warning-card-container': item.workStatus === '1',-->
<!-- 'timing-card-container': item.workStatus === '2',-->
<!-- 'running-card-container': !['1', '2'].includes(item.workStatus),-->
<!-- }"-->
<!-- class="sbjk-card-container common-card-container-body-no-padding common-card-container-no-title-bg"-->
<!-- shadow="always"-->
<!-- >-->
<!-- &lt;!&ndash; 标题&ndash;&gt;-->
<!-- <div slot="header">-->
<!-- <span class="large-title"-->
<!-- >{{ item.deviceName }}</span-->
<!-- >-->
<!-- <div class="info">-->
<!-- <div>-->
<!-- {{-->
<!-- $store.state.ems.workStatusOptions[item.workStatus]-->
<!-- }}-->
<!-- </div>-->
<!-- <div>数据更新时间{{ item.dataUpdateTime }}</div>-->
<!-- </div>-->
<!-- <div class="alarm">-->
<!-- <el-button type="primary" size="small" style="margin-right:20px;" @click="pointDetail(item,'point')">-->
<!-- 详细-->
<!-- </el-button>-->
<!-- <el-badge :value="item.alarmNum || 0" class="item">-->
<!-- <i-->
<!-- class="el-icon-message-solid alarm-icon"-->
<!-- @click="pointDetail(item,'alarmPoint')"-->
<!-- ></i>-->
<!-- </el-badge>-->
<!-- </div>-->
<!-- </div>-->
<!-- &lt;!&ndash; 工作状态&ndash;&gt;-->
<!-- <div class="descriptions-main">-->
<!-- <el-descriptions :colon="false" :column="5" direction="vertical">-->
<!-- <el-descriptions-item-->
<!-- :contentClassName="`descriptions-direction ${-->
<!-- item.workStatus === '0' ? 'save' : 'danger'-->
<!-- }`"-->
<!-- :span="1"-->
<!-- label="工作状态"-->
<!-- labelClassName="descriptions-label"-->
<!-- >{{-->
<!-- $store.state.ems.workStatusOptions[item.workStatus]-->
<!-- }}-->
<!-- </el-descriptions-item-->
<!-- >-->
<!-- <el-descriptions-item-->
<!-- :span="1"-->
<!-- contentClassName="descriptions-direction"-->
<!-- label="工作模式"-->
<!-- labelClassName="descriptions-label"-->
<!-- >-->
<!-- todo 手动/自动-->
<!-- {{-->
<!-- $store.state.ems.gridStatusOptions[item.gridStatus]-->
<!-- }}-->
<!-- </el-descriptions-item-->
<!-- >-->
<!-- <el-descriptions-item-->
<!-- :span="1"-->
<!-- contentClassName="descriptions-direction"-->
<!-- label="并网状态"-->
<!-- labelClassName="descriptions-label"-->
<!-- >{{-->
<!-- $store.state.ems.gridStatusOptions[item.gridStatus]-->
<!-- }}-->
<!-- </el-descriptions-item-->
<!-- >-->
<!-- <el-descriptions-item-->
<!-- :span="1"-->
<!-- contentClassName="descriptions-direction"-->
<!-- label="告警状态"-->
<!-- labelClassName="descriptions-label"-->
<!-- >-->
<!-- todo-->
<!-- &lt;!&ndash;-->
<!-- {{-->
<!-- $store.state.ems.warnOptions[item.warnMode]-->
<!-- }}&ndash;&gt;-->
<!-- </el-descriptions-item-->
<!-- >-->
<!-- <el-descriptions-item-->
<!-- :contentClassName="`descriptions-direction ${-->
<!-- item.deviceStatus === '2' ? 'save' : 'danger'-->
<!-- }`"-->
<!-- :span="1"-->
<!-- label="设备状态"-->
<!-- labelClassName="descriptions-label"-->
<!-- >{{-->
<!-- $store.state.ems.deviceStatusOptions[item.deviceStatus]-->
<!-- }}-->
<!-- </el-descriptions-item-->
<!-- >-->
<!-- </el-descriptions>-->
<!-- </div>-->
<!-- &lt;!&ndash; 用电量&ndash;&gt;-->
<!-- <div class="descriptions-main descriptions-main-bg-color">-->
<!-- <el-descriptions :colon="false" :column="5" direction="vertical">-->
<!-- <el-descriptions-item-->
<!-- :span="5"-->
<!-- contentClassName="descriptions-direction"-->
<!-- label="当日用电量:"-->
<!-- labelClassName="descriptions-label"-->
<!-- >-->
<!-- </el-descriptions-item-->
<!-- >-->
<!-- <el-descriptions-item-->
<!-- :span="1"-->
<!-- contentClassName="descriptions-direction"-->
<!-- label="电网用电量"-->
<!-- labelClassName="descriptions-label"-->
<!-- >-->
<!-- </el-descriptions-item-->
<!-- >-->
<!-- <el-descriptions-item-->
<!-- :span="1"-->
<!-- contentClassName="descriptions-direction"-->
<!-- label="储能放电量"-->
<!-- labelClassName="descriptions-label"-->
<!-- >-->
<!-- </el-descriptions-item-->
<!-- >-->
<!-- <el-descriptions-item-->
<!-- :span="1"-->
<!-- contentClassName="descriptions-direction"-->
<!-- label="储能充电量"-->
<!-- labelClassName="descriptions-label"-->
<!-- >-->
<!-- </el-descriptions-item-->
<!-- >-->
<!-- <el-descriptions-item-->
<!-- :span="1"-->
<!-- contentClassName="descriptions-direction"-->
<!-- label="负荷用电量"-->
<!-- labelClassName="descriptions-label"-->
<!-- >-->
<!-- </el-descriptions-item-->
<!-- >-->
<!-- <el-descriptions-item-->
<!-- :span="1"-->
<!-- contentClassName="descriptions-direction"-->
<!-- label="光伏发电量"-->
<!-- labelClassName="descriptions-label"-->
<!-- >-->
<!-- </el-descriptions-item-->
<!-- >-->
<!-- </el-descriptions>-->
<!-- </div>-->
<!-- &lt;!&ndash; 表格&ndash;&gt;-->
<!-- <el-table-->
<!-- class="common-table"-->
<!-- stripe-->
<!-- style="width: 100%;margin-top:25px;">-->
<!-- <el-table-column-->
<!-- label="功率"-->
<!-- prop="type">-->
<!-- </el-table-column>-->
<!-- <el-table-column-->
<!-- label="电网"-->
<!-- >-->
<!-- <template slot-scope="scope">-->
<!-- <span class="pointer"-->
<!-- @click="showChart('簇电压',scope.row.clusterId)">{{ scope.row.clusterVoltage }} V</span>-->
<!-- </template>-->
<!-- </el-table-column>-->
<!-- <el-table-column-->
<!-- label="储能">-->
<!-- <template slot-scope="scope">-->
<!-- <span class="pointer"-->
<!-- @click="showChart('簇电流',scope.row.clusterId)">{{ scope.row.clusterCurrent }} A</span>-->
<!-- </template>-->
<!-- </el-table-column>-->
<!-- <el-table-column-->
<!-- label="负荷">-->
<!-- <template slot-scope="scope">-->
<!-- <span class="pointer"-->
<!-- @click="showChart('簇电流',scope.row.clusterId)">{{ scope.row.clusterCurrent }} A</span>-->
<!-- </template>-->
<!-- </el-table-column>-->
<!-- <el-table-column-->
<!-- label="光伏">-->
<!-- <template slot-scope="scope">-->
<!-- <span class="pointer"-->
<!-- @click="showChart('当前SOC',scope.row.clusterId)">{{ scope.row.currentSoc }} %</span>-->
<!-- </template>-->
<!-- </el-table-column>-->
<!-- </el-table>-->
<!-- &lt;!&ndash; 图表&ndash;&gt;-->
<!-- <div id="emsChart" style="height: 350px"></div>-->
<!-- </el-card>-->
<!-- </div>-->
<el-card <el-card
v-for="(item,index) in list" v-for="(item,index) in list"
:key="index+'emsList'" :key="index+'emsList'"
@ -220,7 +19,7 @@
<div class="alarm"> <div class="alarm">
<el-button size="small" round style="margin-right:20px;" type="primary" @click="pointDetail(item,'point')">详细 <el-button size="small" round style="margin-right:20px;" type="primary" @click="pointDetail(item,'point')">详细
</el-button> </el-button>
<el-badge :value="item.alarmNum || 0" class="item"> <el-badge :hidden="!item.alarmNum" :value="item.alarmNum || 0" class="item">
<i <i
class="el-icon-message-solid alarm-icon" class="el-icon-message-solid alarm-icon"
@click="pointDetail(item,'alarmPoint')" @click="pointDetail(item,'alarmPoint')"

View File

@ -19,13 +19,14 @@
</keep-alive> </keep-alive>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import getQuerySiteId from "@/mixins/ems/getQuerySiteId"; import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
import { dzjk } from '@/router/ems' import { dzjk } from '@/router/ems'
import {mapState} from "vuex"; import {mapState} from "vuex";
const childrenRoute = ((dzjk[0]?.children || []).find(item=> item.name==='DzjkSbjk').children) || []//获取到单站监控-设备监控下面的字路由 const childrenRoute = dzjk[0].children[0].children.find(item=> item.name==='DzjkSbjk').children//获取到单站监控-设备监控下面的字路由
export default { export default {
name:'DzjkSbjk', name:'DzjkSbjk',
mixins:[getQuerySiteId], mixins:[getQuerySiteId],
@ -64,14 +65,11 @@ export default {
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.sbjk-ems-dashboard-editor-container { .sbjk-ems-content-container{
display: flex; margin-top:0;
background: #ffffff; padding-top:0;
}
.sbjk-ems-content-container {
margin-top: 0;
padding-top: 0;
padding-right: 0; padding-right: 0;
flex: 1; flex: 1;
} }
</style> </style>

View File

@ -8,11 +8,7 @@
style="margin-bottom: 25px" style="margin-bottom: 25px"
> >
<el-card <el-card
:class="{ :class="handleCardClass(pcsItem)"
'timing-card-container':!['0','2'].includes(pcsItem.workStatus),
'warning-card-container':pcsItem.workStatus === '2',
'running-card-container':pcsItem.workStatus === '0'
}"
class="sbjk-card-container common-card-container-body-no-padding common-card-container-no-title-bg" class="sbjk-card-container common-card-container-body-no-padding common-card-container-no-title-bg"
shadow="always" shadow="always"
> >
@ -31,17 +27,11 @@
<div>数据更新时间{{ pcsItem.dataUpdateTime }}</div> <div>数据更新时间{{ pcsItem.dataUpdateTime }}</div>
</div> </div>
<div class="alarm"> <div class="alarm">
<pcs-switch style="margin-right:10px;"
:round="true"
size="small"
type="danger"
:data="pcsItem"
@updateSuccess="init"/>
<el-button type="primary" round size="small" style="margin-right:20px;" <el-button type="primary" round size="small" style="margin-right:20px;"
@click="pointDetail(pcsItem,'point')"> @click="pointDetail(pcsItem,'point')">
详细 详细
</el-button> </el-button>
<el-badge :value="pcsItem.alarmNum || 0" class="item"> <el-badge :hidden="!pcsItem.alarmNum" :value="pcsItem.alarmNum || 0" class="item">
<i <i
class="el-icon-message-solid alarm-icon" class="el-icon-message-solid alarm-icon"
@click="pointDetail(pcsItem,'alarmPoint')" @click="pointDetail(pcsItem,'alarmPoint')"
@ -52,14 +42,12 @@
<div class="descriptions-main"> <div class="descriptions-main">
<el-descriptions :colon="false" :column="4" direction="vertical"> <el-descriptions :colon="false" :column="4" direction="vertical">
<el-descriptions-item <el-descriptions-item
:contentClassName="`descriptions-direction ${ contentClassName="descriptions-direction work-status"
pcsItem.workStatus === '0' ? 'save' : 'danger'
}`"
:span="1" :span="1"
label="工作状态" label="工作状态"
labelClassName="descriptions-label" labelClassName="descriptions-label"
>{{ >{{
$store.state.ems.workStatusOptions[pcsItem.workStatus] PCSWorkStatusOptions[pcsItem.workStatus]
}} }}
</el-descriptions-item </el-descriptions-item
> >
@ -196,12 +184,17 @@ import RealTimeBaseInfo from "./../RealTimeBaseInfo.vue";
import getQuerySiteId from "@/mixins/ems/getQuerySiteId"; import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
import {getPcsDetailInfo, getRunningHeadInfo} from "@/api/ems/dzjk"; import {getPcsDetailInfo, getRunningHeadInfo} from "@/api/ems/dzjk";
import intervalUpdate from "@/mixins/ems/intervalUpdate"; import intervalUpdate from "@/mixins/ems/intervalUpdate";
import PcsSwitch from "@/views/ems/site/sblb/PcsSwitch.vue"; import {mapState} from "vuex";
export default { export default {
name: "DzjkSbjkPcs", name: "DzjkSbjkPcs",
components: {RealTimeBaseInfo, pointChart, PointTable, PcsSwitch}, components: {RealTimeBaseInfo, pointChart, PointTable},
mixins: [getQuerySiteId, intervalUpdate], mixins: [getQuerySiteId, intervalUpdate],
computed: {
...mapState({
PCSWorkStatusOptions: state => state?.ems?.PCSWorkStatusOptions || {},
})
},
data() { data() {
return { return {
loading: false, loading: false,
@ -288,6 +281,10 @@ export default {
}; };
}, },
methods: { methods: {
handleCardClass(item) {
const {workStatus = ''} = item
return workStatus === '1' || !Object.keys(this.PCSWorkStatusOptions).find(i => i === workStatus) ? "timing-card-container" : workStatus === '2' ? 'warning-card-container' : 'running-card-container'
},
// 查看设备电位表格 // 查看设备电位表格
pointDetail(row, dataType) { pointDetail(row, dataType) {
const {deviceId} = row const {deviceId} = row

View File

@ -6,7 +6,7 @@
<div slot="header"> <div slot="header">
<span class="card-title">PCS有功功率/PCS无功功率</span> <span class="card-title">PCS有功功率/PCS无功功率</span>
</div> </div>
<div style="height: 360px" id="cnglqxChart" /> <div style="height: 360px" id="cnglqxChart"/>
</el-card> </el-card>
</template> </template>
@ -14,7 +14,7 @@
<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 { storagePower } from "@/api/ems/dzjk"; import {storagePower} from "@/api/ems/dzjk";
export default { export default {
mixins: [resize], mixins: [resize],
@ -36,8 +36,8 @@ export default {
methods: { methods: {
init(siteId, timeRange) { init(siteId, timeRange) {
this.chart.showLoading(); this.chart.showLoading();
const [startTime='', endTime=''] = timeRange; const [startTime = '', endTime = ''] = timeRange;
storagePower(siteId,startTime,endTime) storagePower(siteId, startTime, endTime)
.then((response) => { .then((response) => {
this.setOption(response?.data?.pcsPowerList || []); this.setOption(response?.data?.pcsPowerList || []);
}) })
@ -62,8 +62,8 @@ export default {
data: (element.energyStoragePowList || []).map( data: (element.energyStoragePowList || []).map(
(i) => { (i) => {
return { return {
value:i.pcsTotalActPower, value: i.pcsTotalActPower,
year:i.dateDay || '' year: i.dateDay || ''
} }
} }
) )
@ -78,14 +78,14 @@ export default {
(i) => { (i) => {
return { return {
value: i.pcsTotalReactivePower, value: i.pcsTotalReactivePower,
year:i.dateDay || '' year: i.dateDay || ''
} }
} }
), ),
} }
); );
}); });
this.chart.setOption({ this.chart && this.chart.setOption({
legend: { legend: {
left: "center", left: "center",
top: "5", top: "5",
@ -99,29 +99,29 @@ export default {
containLabel: true, containLabel: true,
}, },
tooltip: { tooltip: {
show:true, show: true,
trigger: "axis", trigger: "axis",
axisPointer: { axisPointer: {
// 坐标轴指示器,坐标轴触发有效 // 坐标轴指示器,坐标轴触发有效
type: "shadow", // 默认为直线,可选为:'line' | 'shadow' type: "shadow", // 默认为直线,可选为:'line' | 'shadow'
}, },
formatter :(params)=>{ formatter: (params) => {
if(params.length <= 0) return if (params.length <= 0) return
let result = (params[0].data.year || '')+' '+ params[0].name + '<div>' let result = (params[0].data.year || '') + ' ' + params[0].name + '<div>'
params.forEach(item=>{ params.forEach(item => {
const {color,seriesName,value} = item const {color, seriesName, value} = item
result += `<div style="position: relative;padding-left:20px;line-height: 20px;"> result += `<div style="position: relative;padding-left:20px;line-height: 20px;">
<div style="position: absolute;top:50%;left:0;width:12px;height:12px;border-radius:100%;background: ${color};transform: translateY(-50%)"></div> <div style="position: absolute;top:50%;left:0;width:12px;height:12px;border-radius:100%;background: ${color};transform: translateY(-50%)"></div>
<span>${seriesName}</span><span style="margin-left:20px;font-weight: 700">${value}</span></div>` <span>${seriesName}</span><span style="margin-left:20px;font-weight: 700">${value}</span></div>`
}) })
result+='</div>' result += '</div>'
return result return result
} }
}, },
textStyle: { textStyle: {
color: "#333333", color: "#333333",
}, },
xAxis: { type: "category", data: xdata }, xAxis: {type: "category", data: xdata},
yAxis: { yAxis: {
type: "value", type: "value",
}, },
@ -137,7 +137,7 @@ export default {
}, },
], ],
series, series,
}); }, true);
}, },
}, },
}; };

View File

@ -54,7 +54,7 @@ export default {
year:element.dateDay, year:element.dateDay,
}); });
}); });
xdata = this.chart.setOption({ this.chart && this.chart.setOption({
legend: { legend: {
left: "center", left: "center",
top: "5", top: "5",
@ -115,7 +115,7 @@ export default {
data: ydata, data: ydata,
}, },
], ],
}); },true);
}, },
}, },
}; };

View File

@ -57,7 +57,7 @@ export default {
} }
); );
}); });
xdata = this.chart.setOption({ this.chart && this.chart.setOption({
legend: { legend: {
left: "center", left: "center",
top: "5", top: "5",
@ -118,7 +118,7 @@ export default {
data: ydata, data: ydata,
}, },
], ],
}); },true);
}, },
}, },
}; };

View File

@ -66,7 +66,7 @@ export default {
}), }),
}); });
}); });
this.chart.setOption({ this.chart && this.chart.setOption({
legend: { legend: {
left: "center", left: "center",
top: "5", top: "5",
@ -118,7 +118,7 @@ export default {
}, },
], ],
series, series,
}); },true);
}, },
}, },
}; };

View File

@ -25,7 +25,7 @@
<el-button type="primary" round size="small" style="margin-right:20px;" @click="pointDetail(item,'point')"> <el-button type="primary" round size="small" style="margin-right:20px;" @click="pointDetail(item,'point')">
详细 详细
</el-button> </el-button>
<el-badge :value="item.alarmNum || 0" class="item"> <el-badge :hidden="!item.alarmNum" :value="item.alarmNum || 0" class="item">
<i <i
class="el-icon-message-solid alarm-icon" class="el-icon-message-solid alarm-icon"
@click="pointDetail(item,'alarmPoint')" @click="pointDetail(item,'alarmPoint')"

View File

@ -14,7 +14,7 @@
<el-button type="primary" round size="small" style="margin-right:20px;" @click="pointDetail(item,'point')"> <el-button type="primary" round size="small" style="margin-right:20px;" @click="pointDetail(item,'point')">
详细 详细
</el-button> </el-button>
<el-badge :value="item.alarmNum || 0" class="item"> <el-badge :hidden="!item.alarmNum" :value="item.alarmNum || 0" class="item">
<i <i
class="el-icon-message-solid alarm-icon" class="el-icon-message-solid alarm-icon"
@click="pointDetail(item,'alarmPoint')" @click="pointDetail(item,'alarmPoint')"

View File

@ -7,55 +7,47 @@
text-color="#666666" text-color="#666666"
active-text-color="#ffffff" active-text-color="#ffffff"
> >
<el-menu-item <el-menu-item :index="item.name" v-for="(item,index) in childrenRoute" :key="index+'tjbbChildrenRoute'">
:index="item.name" <router-link style="height: 100%;width: 100%;display: block;" :to="{path:item.path,query:$route.query}">
v-for="(item, index) in childrenRoute" {{item.meta.title}}
: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 <div class="ems-content-container ems-content-container-padding tjbb-ems-content-container">
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( const childrenRoute = dzjk[0].children[0].children.find(item=> item.name==='DzjkTjbb').children//获取到统计报表下面的字路由
(item) => item.name === "DzjkTjbb" console.log('设备监控子路由',childrenRoute)
).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-content-container { .tjbb-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

@ -27,15 +27,14 @@
class="common-table" class="common-table"
:data="tableData" :data="tableData"
show-summary show-summary
:summary-method="getSummaries"
stripe stripe
style="width: 100%;margin-top:25px;"> style="width: 100%;margin-top:25px;">
<!-- 汇总列--> <!-- 汇总列-->
<el-table-column label="汇总" min-width="180px" align="center"> <el-table-column label="汇总" min-width="100px" align="center">
<el-table-column <el-table-column
prop="dataTime" prop="dataTime"
label="日期" label="日期"
min-width="180px" align="center"> min-width="100px" align="center">
</el-table-column> </el-table-column>
</el-table-column> </el-table-column>
<!--充电量列--> <!--充电量列-->
@ -94,6 +93,14 @@
label="总"> label="总">
</el-table-column> </el-table-column>
</el-table-column> </el-table-column>
<!-- 实际收益-->
<el-table-column label="" align="center" fixed="right">
<el-table-column
prop="actualRevenue"
label="实际收益"
align="center">
</el-table-column>
</el-table-column>
</el-table> </el-table>
<el-pagination <el-pagination
v-show="tableData.length>0" v-show="tableData.length>0"
@ -137,46 +144,6 @@ export default {
} }
}, },
methods: { methods: {
//表格汇总
getSummaries(param) {
const {columns, data} = param;
const sums = [];
columns.forEach((column, index) => {
if (index === 0) {
const activeTotal = data.map(item => item.activeTotalPrice).reduce((prev, curr) => {
const value = Number(curr);
if (!isNaN(value)) {
return prev + curr;
} else {
return prev;
}
}, 0);
const reActiveTotal = data.map(item => item.reActiveTotalPrice).reduce((prev, curr) => {
const value = Number(curr);
if (!isNaN(value)) {
return prev + curr;
} else {
return prev;
}
}, 0);
sums[index] = '价差收入 : ' + (reActiveTotal - activeTotal);
return
}
const values = data.map(item => Number(item[column.property]));
if (!values.every(value => isNaN(value))) {
sums[index] = values.reduce((prev, curr) => {
const value = Number(curr);
if (!isNaN(value)) {
return prev + curr;
} else {
return prev;
}
}, 0);
}
})
return sums
},
// 搜索 // 搜索
onSearch() { onSearch() {
this.pageNum = 1//每次搜索从1开始搜索 this.pageNum = 1//每次搜索从1开始搜索

View File

@ -1,577 +0,0 @@
<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

@ -1,614 +0,0 @@
<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>

View File

@ -323,7 +323,7 @@ $lineColor: #86bcc7;
.ems-dashboard-editor-container { .ems-dashboard-editor-container {
background-color: #ffffff; background-color: #ffffff;
padding: 30px; padding: 0;
color: #666666; color: #666666;
.container { .container {

View File

@ -80,7 +80,7 @@
<el-table-column label="数据点位" prop="dataPoint"></el-table-column> <el-table-column label="数据点位" prop="dataPoint"></el-table-column>
<el-table-column <el-table-column
label="数据点位名称" label="数据点位名称"
prop="pointName" prop="dataPointName"
></el-table-column> ></el-table-column>
<!-- <el-table-column label="modbus地址">--> <!-- <el-table-column label="modbus地址">-->
<!-- <template slot-scope="scope">--> <!-- <template slot-scope="scope">-->
@ -88,8 +88,8 @@
<!-- `${scope.row.ipAddress || ""} ${scope.row.ipPort || ""}`--> <!-- `${scope.row.ipAddress || ""} ${scope.row.ipPort || ""}`-->
<!-- }}</span>--> <!-- }}</span>-->
<!-- </template>--> <!-- </template>-->
<!-- </el-table-column>--> <!-- </el-table-column>
<el-table-column label="寄存器地址" prop="寄存器地址"></el-table-column> <el-table-column label="寄存器地址" prop="寄存器地址"></el-table-column>-->
<el-table-column <el-table-column
label="最新值" label="最新值"
prop="pointValue" prop="pointValue"

View File

@ -84,10 +84,10 @@
label="在线状态"> label="在线状态">
<template slot-scope="scope"> <template slot-scope="scope">
<span>{{ $store.state.ems.deviceStatusOptions[scope.row.deviceStatus] }}</span> <span>{{ $store.state.ems.deviceStatusOptions[scope.row.deviceStatus] }}</span>
<pcs-switch v-if="scope.row.deviceCategory === 'PCS' && ![null,'',undefined].includes(scope.row.workStatus)" <!-- <pcs-switch v-if="scope.row.deviceCategory === 'PCS' && ![null,'',undefined].includes(scope.row.deviceStatus)"-->
style="margin-left:5px;" <!-- style="margin-left:5px;"-->
:data="{siteId:scope.row.siteId,workStatus:scope.row.workStatus,deviceId:scope.row.deviceId,deviceName:scope.row.deviceName}" <!-- :data="{siteId:scope.row.siteId,deviceStatus:scope.row.deviceStatus,deviceId:scope.row.deviceId,deviceName:scope.row.deviceName}"-->
@updateSuccess="getData"/> <!-- @updateSuccess="getData"/>-->
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column
@ -180,11 +180,11 @@ import {getAllDeviceCategory} from '@/api/ems/search'
import PointTable from './PointTable.vue' import PointTable from './PointTable.vue'
import AddDevice from "./AddDevice.vue"; import AddDevice from "./AddDevice.vue";
import PointUpload from "./PointUpload.vue"; import PointUpload from "./PointUpload.vue";
import PcsSwitch from "./PcsSwitch.vue"; // import PcsSwitch from "./PcsSwitch.vue";
export default { export default {
name: "Sblb", name: "Sblb",
components: {AddDevice, PointTable, PointUpload, PcsSwitch}, components: {AddDevice, PointTable, PointUpload},
data() { data() {
return { return {
loading: false, loading: false,

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 MiB

418
src/views/screen/index.vue Normal file
View File

@ -0,0 +1,418 @@
<template>
<div class="big-screen-container">
<!-- 背景容器 -->
<div class="bg-wrapper">
<!-- 右侧功率曲线图表 -->
<div class="chart-box power-chart">
<div ref="powerChart" class="chart-content"></div>
</div>
<!-- 底部中间电力需求曲线图表width:980px, bottom:26px -->
<div class="chart-box demand-chart">
<div ref="demandChart" class="chart-content"></div>
</div>
</div>
</div>
</template>
<script>
import * as echarts from 'echarts'
import { getPointData } from '@/api/ems/dzjk'
export default {
name: 'PowerCurveScreen',
data() {
return {
powerChartInstance: null, // 功率曲线实例
demandChartInstance: null, // 电力需求曲线实例
timer: null,
siteId: '021_DDS_01',
}
},
mounted() {
this.initPowerChart() // 初始化功率曲线
this.initDemandChart() // 初始化电力需求曲线
// Initial fetch
this.fetchData()
// Polling every 5 seconds
this.timer = setInterval(() => {
this.fetchData()
}, 5000)
window.addEventListener('resize', this.resizeAllCharts)
},
beforeDestroy() {
if (this.timer) {
clearInterval(this.timer)
this.timer = null
}
// 销毁两个图表实例
if (this.powerChartInstance) {
this.powerChartInstance.dispose()
this.powerChartInstance = null
}
if (this.demandChartInstance) {
this.demandChartInstance.dispose()
this.demandChartInstance = null
}
window.removeEventListener('resize', this.resizeAllCharts)
},
methods: {
getTodayDate() {
const now = new Date()
const year = now.getFullYear()
const month = String(now.getMonth() + 1).padStart(2, '0')
const day = String(now.getDate()).padStart(2, '0')
return `${year}-${month}-${day}`
},
fetchData() {
const today = this.getTodayDate()
getPointData({
siteId: this.siteId,
startDate: today,
endDate: today
}).then(response => {
if (response.code === 200 && response.data) {
this.updateCharts(response.data)
}
}).catch(error => {
console.error('Failed to fetch point data:', error)
})
},
updateCharts(data) {
if (!data || !Array.isArray(data)) return
const source = [['日期', '电网功率', '负载功率', '储能功率', '光伏功率']];
const demandSource = [['日期', '电网功率', '负载功率']]
data.forEach(item => {
source.push([
item.statisDate,
item.gridPower,
item.loadPower,
item.storagePower,
item.pvPower
]);
demandSource.push([
item.statisDate,
item.gridPower, // Mapping Grid Power to Plan Demand (or just as 2nd series)
item.loadPower, // Mapping Load Power to Actual Demand
])
})
if (this.powerChartInstance) {
this.powerChartInstance.setOption({
dataset: {
source: source
}
})
}
if (this.demandChartInstance) {
this.demandChartInstance.setOption({
dataset: {
source: demandSource
}
})
}
},
// 初始化右侧功率曲线
initPowerChart() {
this.powerChartInstance = echarts.init(this.$refs.powerChart)
const option = {
backgroundColor: 'transparent',
tooltip: {
trigger: 'axis',
confine: true,
backgroundColor: 'rgba(0, 30, 60, 0.9)',
borderColor: '#00ccff',
textStyle: { color: '#fff' },
axisPointer: {
type: 'line',
lineStyle: {
color: 'rgba(0, 204, 255, 0.5)',
type: 'dashed'
}
}
},
legend: {
textStyle: { color: '#00ccff', fontSize: 10 },
bottom: 0,
left: 'center',
itemWidth: 9,
itemHeight: 9,
itemGap: 8,
padding: [0, 0, 0, 0]
},
grid: {
left: '2%',
right: '2%',
top: '15%',
bottom: '12%',
containLabel: true
},
dataset: {
source: [] // Initial empty source
},
xAxis: {
type: 'category',
boundaryGap: false,
axisLabel: {
color: '#00ccff',
fontSize: 10,
rotate: 0, // 取消旋转,因为标签较少
margin: 10,
},
axisLine: {
show: true,
lineStyle: { color: '#006699', width: 1.5 }
},
splitLine: { show: false },
axisTick: { show: true }
},
yAxis: {
type: 'value',
min: 0, // Remove fixed min/max to let it auto-scale
max: 350,
interval: 50,
axisLabel: {
color: '#00ccff',
fontSize: 12,
margin: 10,
fontWeight: 'bold'
},
axisLine: {
show: false,
lineStyle: { color: '#006699', width: 1.5 }
},
splitLine: { lineStyle: { color: 'rgba(0, 100, 150, 0.2)' } },
axisTick: { show: false }
},
series: [
{
// name: '电网功率', // Removed, auto-matched from dataset header
type: 'line',
// data: [], // Removed
smooth: true,
symbol: 'none',
lineStyle: { color: '#0099ff', width: 2 },
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(0, 153, 255, 0.4)' },
{ offset: 1, color: 'rgba(0, 153, 255, 0.05)' }
])
}
},
{
// name: '负载功率',
type: 'line',
// data: [],
smooth: true,
symbol: 'none',
lineStyle: { color: '#00cc66', width: 2 },
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(0, 204, 102, 0.4)' },
{ offset: 1, color: 'rgba(0, 204, 102, 0.05)' }
])
}
},
{
// name: '储能功率',
type: 'line',
// data: [],
smooth: true,
symbol: 'none',
lineStyle: { color: '#ffcc00', width: 2 },
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(255, 204, 0, 0.4)' },
{ offset: 1, color: 'rgba(255, 204, 0, 0.05)' }
])
}
},
{
// name: '光伏功率',
type: 'line',
// data: [],
smooth: true,
symbol: 'none',
lineStyle: { color: '#ff3333', width: 2 },
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(255, 51, 51, 0.4)' },
{ offset: 1, color: 'rgba(255, 51, 51, 0.05)' }
])
}
}
]
}
this.powerChartInstance.setOption(option)
},
// 初始化底部中间电力需求曲线适配980px宽度+130px高度+bottom:26px两条曲线区分配色
initDemandChart() {
this.demandChartInstance = echarts.init(this.$refs.demandChart)
const option = {
backgroundColor: 'transparent', // 透明背景
tooltip: {
trigger: 'axis',
backgroundColor: 'rgba(0, 30, 60, 0.9)',
borderColor: '#00ccff', // 匹配图片主色
textStyle: { color: '#fff' },
axisPointer: { type: 'shadow' },
confine: true,
},
grid: {
left: '2%',
right: '2%',
top: '5%',
bottom: '8%',
containLabel: true
},
dataset: {
source: [] // Initial empty
},
xAxis: {
type: 'category',
boundaryGap: false,
// data: [], // Handled by dataset
axisLabel: {
color: '#00ccff', // 匹配图片主色
fontSize: 10,
margin: 10,
},
axisLine: {
show: true,
lineStyle: { color: '#006699', width: 1.5 }
},
splitLine: { show: false },
axisTick: { show: true }
},
yAxis: {
type: 'value',
min: 0,
max: 300,
interval: 50,
axisLabel: {
color: '#00ccff', // 匹配图片主色
fontSize: 12,
margin: 10,
fontWeight: 'bold'
},
axisLine: {
show: false,
// lineStyle: { color: '#006699', width: 1.5 }
},
splitLine: { lineStyle: { color: 'rgba(0, 100, 150, 0.2)' } },
axisTick: { show: false }
},
series: [
{
type: 'line',
// data: [],
smooth: true, // 平滑曲线
symbol: 'none', // 无数据点
// lineStyle: { color: '#00F2FF', width: 2.5 }, // 匹配图片主题色
// areaStyle: {
// // 匹配图片的线性渐变:#00F2FF 从100%不透明到0%不透明
// color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
// { offset: 0, color: 'rgba(0, 242, 255, 1)' }, // 0%位置:#00F2FF 100%不透明
// { offset: 1, color: 'rgba(0, 242, 255, 0)' } // 100%位置:#00F2FF 0%不透明
// ])
// }
lineStyle: { color: '#0099ff', width: 2 },
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(0, 153, 255, 0.4)' },
{ offset: 1, color: 'rgba(0, 153, 255, 0.05)' }
])
}
},
{
// name: '负载功率',
type: 'line',
// data: [],
smooth: true,
symbol: 'none',
lineStyle: { color: '#00cc66', width: 2 },
areaStyle: {
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: 'rgba(0, 204, 102, 0.4)' },
{ offset: 1, color: 'rgba(0, 204, 102, 0.05)' }
])
}
},
]
}
this.demandChartInstance.setOption(option)
},
// 统一处理窗口缩放(适配两个图表)
resizeAllCharts() {
if (this.powerChartInstance) this.powerChartInstance.resize()
if (this.demandChartInstance) this.demandChartInstance.resize()
}
}
}
</script>
<style scoped>
/* 大屏容器固定1920x1080尺寸 */
.big-screen-container {
width: 1920px;
height: 1080px;
margin: 0;
padding: 0;
overflow: hidden;
position: relative;
}
/* 背景容器:相对路径 ./background.png */
.bg-wrapper {
width: 100%;
height: 100%;
background-image: url("./background.png");
background-size: cover;
background-position: center;
background-repeat: no-repeat;
position: relative;
}
/* 通用图表容器样式 */
.chart-box {
background-color: transparent;
padding: 0;
border: none;
box-shadow: none;
border-radius: 0;
z-index: 10;
}
/* 右侧功率曲线图表定位+尺寸 */
.power-chart {
position: absolute;
top: 143px;
right: 40px;
width: 380px;
height: 270px;
}
/* 底部中间电力需求曲线图表核心修改width:980px, bottom:26px, height:130px */
.demand-chart {
position: absolute;
bottom: 26px; /* 修改为26px底部间距 */
left: 50%;
transform: translateX(-50%);
width: 980px; /* 修改为980px宽度 */
height: 130px; /* 保持130px高度不变 */
}
/* 图表内容区:填满容器 */
.chart-content {
width: 100%;
height: 100%;
}
</style>