Files
energy_storage/web/src/components/monitor/device.vue
2025-09-11 16:14:55 +08:00

518 lines
12 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="device" ref="device">
<div class="device-item" v-for="item in deviceList" :key="item">
<div class="item-header">
<div style="display: flex; width: 50%">
<div class="icon-bg">
<span class="iconfont" :class="getIcongont(item)"></span>
</div>
<div class="title">
<span class="number">{{ item.device_id }}</span>
<span class="name">{{ item.name }}</span>
<span class="number type">{{ item.typename }}</span>
</div>
</div>
<div class="status">
<div class="status-item">
<span>{{ ['离线', '在线'][item.is_online] }}</span>
<span class="text">在线状态</span>
</div>
<div class="status-item">
<span>{{ ['正常', '错误'][item.is_error] }}</span>
<span class="text">故障状态</span>
</div>
<div class="status-item">
<span>{{ ['空闲', '工作'][item.is_running] }}</span>
<span class="text">工作状态</span>
</div>
</div>
</div>
<div class="item-content">
<div v-for="info in item.params" :key="info.k" class="item-info">
<span class="text">{{ info.k }}</span>
<span class="value">{{ info.v }}</span>
</div>
</div>
<div class="item-button">
<div v-if="item.view == 1">
<span class="text">实时数据</span>
<a-button type="primary" size="small" @click="openModal(item, 1)">查看</a-button>
</div>
<div v-if="item.type == 105">
<span class="text">单体信息</span>
<a-button type="primary" size="small" @click="openModal(item, 2)">查看</a-button>
</div>
</div>
</div>
<a-modal
v-model:open="modalOpen"
@ok="handleOk"
width="60%"
class="modal-device"
:get-container="() => $refs.device"
>
<div v-if="modalComponent == 1" class="modal-content">
<div class="item">
<div class="title">
电流电压
<!-- <div>电流电压</div>
<img src="@/assets/images/titleLine.png" alt="" /> -->
</div>
<div class="echarts">
<predictEcharts
:chart-options="chartOptions[0]"
:chart-data="chartData"
ref="chartRef1"
/>
</div>
</div>
<div class="item">
<div class="title">功率</div>
<div class="echarts">
<predictEcharts
:chart-options="chartOptions[1]"
:chart-data="chartData"
ref="chartRef2"
/>
</div>
</div>
</div>
<div class="content-table" style="height: 600px" v-else>
<a-table
:columns="columns"
:data-source="tableData"
size="small"
:scroll="{ y: 500 }"
:pagination="false"
row-class-name="no-hover-row"
row-key="id"
>
</a-table>
</div>
</a-modal>
</div>
</template>
<script>
import predictEcharts from '@/components/predict/predictEcharts.vue'
import { postReq, getReq } from '@/request/api'
import { deviceTypeList } from '@/utils/config'
export default {
name: '',
components: { predictEcharts },
props: {
stationId: {
type: String,
default: ''
},
systemType: {
type: Number,
default: 1
}
},
data() {
return {
modalOpen: false,
deviceList: [],
chartOptions: [
{
type: 'line',
smooth: true,
dataKey: 'chargeDischarge',
infoKeys: [
{
key: 'V',
label: '电压',
seriesOptions: {
symbol: 'circle',
symbolSize: 8,
itemStyle: {
color: '#00FDF9' // 充电电量线条颜色
},
lineStyle: {
color: '#00FDF9' // 充电电量线条颜色
}
}
},
{
key: 'I',
label: '电流',
seriesOptions: {
symbol: 'circle',
symbolSize: 8,
itemStyle: {
color: '#3E7EEF' // 充电电量线条颜色
},
lineStyle: {
color: '#3E7EEF' // 充电电量线条颜色
}
}
}
]
},
{
type: 'line',
smooth: true,
dataKey: 'chargeDischarge',
infoKeys: [
{
key: 'P',
label: '功率',
seriesOptions: {
symbol: 'circle',
symbolSize: 8,
itemStyle: {
color: '#00FDF9' // 充电电量线条颜色
},
lineStyle: {
color: '#00FDF9' // 充电电量线条颜色
}
}
}
]
}
],
chartData: {
xdata: [],
ydata: {}
},
modalComponent: 1,
columns: [
{
title: '序号',
dataIndex: 'id',
key: 'id',
ellipsis: true,
fixed: 'left'
},
{
title: '单体SOC%',
dataIndex: 'SOC',
key: 'SOC'
},
{
title: '单体SOH%',
dataIndex: 'SOH',
key: 'SOH'
},
{
title: '单体电压V',
dataIndex: 'V',
key: 'V'
},
{
title: '单体温度(℃)',
dataIndex: 'T',
key: 'T'
},
{
title: '单体内阻(Ω)',
dataIndex: 'R_i',
key: 'R_i'
}
],
tableData: [],
pageOption: {
// pageSize: 1000,
// page: 1
},
tableOption: {
scroll: {
y: 500
},
page: false
}
}
},
watch: {
// 监听父组件数据变化
stationId(newVal) {
if (newVal) {
this.getDeviceList()
}
},
systemType(newVal, oldVal) {
if (newVal !== oldVal) {
this.getDeviceList()
}
}
},
mounted() {
this.getDeviceList()
},
methods: {
handlePagesizeChange(pageOption) {
this.pageOption.pageSize = pageOption.pageSize
this.pageOption.page = pageOption.page
this.getList()
},
getIcongont(ele) {
return deviceTypeList.find((item) => item.value == ele.type).iconfont || ''
},
generateTimePoints() {
const timePoints = []
for (let hour = 0; hour <= 24; hour++) {
for (let minute = 0; minute < 60; minute += 10) {
// 处理24:00的特殊情况
if (hour === 24 && minute === 0) {
timePoints.push('24:00')
break
}
// 格式化小时和分钟,确保两位数
const formattedHour = hour.toString().padStart(2, '0')
const formattedMinute = minute.toString().padStart(2, '0')
timePoints.push(`${formattedHour}:${formattedMinute}`)
}
}
return timePoints
},
//查看实时数据
async getDevicCharts(item) {
try {
const res = await getReq('/queryDevicCharts', {
station_id: this.stationId,
device_id: item.device_id
})
this.chartData.ydata = res.data
this.chartData.xdata = this.generateTimePoints()
this.$nextTick(() => {
if (this.$refs.chartRef1) {
this.$refs.chartRef1.initCharts()
}
if (this.$refs.chartRef2) {
this.$refs.chartRef2.initCharts()
}
})
// this.$refs.chartRef1.initCharts()
// this.$refs.chartRef2.initCharts()
} catch (error) {
console.log(error)
}
},
//查询BCU单体信息
async getDeviceBCUDetail(item) {
try {
const res = await getReq('/queryDeviceBCUDetail', {
station_id: this.stationId,
device_id: item.device_id
})
this.tableData = res.data.map((item, index) => {
item.id = index + 1
return item
})
} catch (error) {
console.log(error)
}
},
//请求运行监控系统设备信息
async getDeviceList() {
try {
const res = await getReq('/queryDevicByCategory', {
station_id: this.stationId,
category: this.systemType
})
this.deviceList = res.data
} catch (error) {
this.deviceList = []
console.log(error)
}
},
async openModal(item, val) {
console.log(item, '=============')
this.modalComponent = val
this.modalOpen = true
if (val == 1) {
await this.getDevicCharts(item)
} else {
await this.getDeviceBCUDetail(item)
}
},
handleOk() {
this.modalOpen = false
}
}
}
</script>
<style lang="scss" scoped>
.device {
width: 100%;
height: 100%;
margin-left: 20px;
display: flex;
flex-wrap: wrap;
grid-gap: 20px;
overflow-y: auto;
align-content: flex-start;
// grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
.device-item {
border-radius: 15px;
background: $bg2-color;
padding: 15px;
min-width: 340px;
max-width: 430px;
height: 260px;
flex: 1;
.item-header {
display: flex;
align-items: center;
height: 70px;
color: #fff;
justify-content: space-between;
.icon-bg {
width: 66px;
height: 66px;
text-align: center;
line-height: 66px;
border-radius: 6px;
background: linear-gradient(90deg, #3dfefa 0%, #2a82e4 2.96%, #27a188 100%),
linear-gradient(90deg, #3dfefa 0%, #01dfef 2.96%, #08a5ff 100%);
.iconfont {
font-size: 45px;
}
}
.title {
display: flex;
flex-direction: column;
justify-content: space-around;
margin-left: 15px;
}
.number {
font-size: 12px;
}
.name {
font-size: 14px;
}
.type {
color: #08a5ff;
}
.status {
display: flex;
font-size: 14px;
height: 100%;
width: 50%;
justify-content: space-between;
&-item {
display: flex;
flex-direction: column;
text-align: center;
justify-content: space-evenly;
span:first-child {
font-weight: 700;
}
.text {
color: $text-color;
}
}
}
}
.item-content {
grid-template-columns: 1fr 1fr;
color: #fff;
display: grid;
margin-top: 15px;
margin-bottom: 3px;
padding: 0 10px;
height: 120px;
overflow-y: auto;
.value {
font-weight: 700;
}
.item-info {
height: 30px;
}
}
.item-button {
width: 100%;
padding-left: 10px;
display: flex;
div {
width: calc(50% - 10px);
}
}
.text {
color: $text-color;
}
.video {
margin-top: 10px;
}
}
.content-table {
height: 700px;
margin-top: 40px;
:deep(.ant-table-header tr th) {
background: $table-border !important;
color: #fff;
}
:deep(.ant-table-body tr td, .ant-table-cell) {
background: $table-bg !important;
color: #fff;
}
}
}
.environment {
width: 200px;
}
.modal-device {
color: #fff;
.modal-content {
height: 700px;
.item {
// height: 300px;
.title {
color: #fff;
width: 230px;
border-bottom: 5px solid transparent;
border-image: url('@/assets/boxBottom.png') 0 0 100% 0 stretch;
}
.echarts {
height: 300px;
}
}
}
}
:deep(.ant-modal-body) {
:deep(.ant-table-wrapper .ant-table-row-expand-icon) {
color: $green !important;
}
//表格行悬浮样式
.no-hover-row:hover > td {
// background-color: transparent !important;
background-color: #f0f8ff !important;
}
}
:deep(.ant-table-cell) {
&::before {
width: 0 !important;
}
}
:deep(.ant-table-thead > tr > td) {
border-bottom: none !important;
border-top: none !important;
}
:deep(.ant-table-wrapper .ant-table-tbody > tr > td) {
border-top: none !important;
}
:deep(.ant-table-wrapper .ant-table-thead > tr > th) {
border-bottom: none !important;
}
</style>