map/ui/dasadmin/src/views/backend/home/windMatrixpage.vue

721 lines
25 KiB
Vue
Raw Normal View History

2024-12-18 13:18:45 +08:00
<template>
<div class="FanList-content">
2024-12-19 15:05:36 +08:00
<div class="overviewPart" v-for="item in props.parentData">
<!-- <el-col :xs="12" :sm="8" :md="8" :lg="4" :xl="2" style="margin-bottom: 5px">-->
<div class="grid-content"
2024-12-18 13:18:45 +08:00
@click="handleClick(item)"
@contextmenu.prevent="windContextMenu($event,item)"
>
<div class="FanList-panel wind-default" :class="{
'': item.standard==1,
'': item.standard==0,
'wind-offline': item.attributeMap.processedoperationmode == 33
}">
<div class="fanlist-top">
<span :class="item.standard == 1 ? 'wind-mark-icon' : 'fanlist-icon'">
<img :class="item.standard == 1 ? '' : 'wind-picture'" src="~assets/dashboard/biaogan.png" alt="" />
</span>
<span class="fanlist-name"> {{ item.name }}</span>
<el-tag v-if="item.attributeMap.processedoperationmode === 20" class="tag-panel" :style="getStyles(item.attributeMap.color)" type="primary">并网</el-tag>
<el-tag v-if="item.attributeMap.processedoperationmode === 11" class="tag-panel" :style="getStyles(item.attributeMap.color)" type="primary">待机</el-tag>
<el-tag v-if="item.attributeMap.processedoperationmode === 16" class="tag-panel" :style="getStyles(item.attributeMap.color)" type="primary">启动</el-tag>
<el-tag v-if="item.attributeMap.processedoperationmode === 10" class="tag-panel" :style="getStyles(item.attributeMap.color)" type="primary"
>维护</el-tag>
<el-tag v-if="item.attributeMap.processedoperationmode === 0" class="tag-panel" :style="getStyles(item.attributeMap.color)" type="primary">离线</el-tag>
<el-tag v-if="item.attributeMap.processedoperationmode === 8" class="tag-panel" :style="getStyles(item.attributeMap.color)" type="primary">限功率运行</el-tag>
<el-tag v-if="item.attributeMap.processedoperationmode === 6" class="tag-panel" :style="getStyles(item.attributeMap.color)" type="primary">正常停机</el-tag>
<el-tag v-if="item.attributeMap.processedoperationmode === 1" class="tag-panel" :style="getStyles(item.attributeMap.color)" type="primary"
>外部因素导致停机</el-tag>
<el-tag v-if="item.attributeMap.processedoperationmode === 2" class="tag-panel" :style="getStyles(item.attributeMap.color)" type="primary">停机</el-tag>
<el-tag v-if="item.attributeMap.processedoperationmode === 1110" class="tag-panel" :style="getStyles(item.attributeMap.color)" type="primary">解缆状态</el-tag>
<el-tag v-if="item.attributeMap.processedoperationmode === 1111" class="tag-panel" :style="getStyles(item.attributeMap.color)" type="primary"
>电网故障停机</el-tag>
<el-tag v-if="item.attributeMap.processedoperationmode === 1112" class="tag-panel" :style="getStyles(item.attributeMap.color)" type="primary"
>安全链停机</el-tag>
<el-tag v-if="item.attributeMap.processedoperationmode === 33" class="tag-panel" :style="getStyles(item.attributeMap.color)" type="primary"
>通讯中断</el-tag>
</div>
<div class="fanlist-main">
2024-12-19 15:05:36 +08:00
<el-row>
<el-col :span="24">
<div class="fanlist-pic">
<div class="mask">
<div class="heart"><img :src="getSafeImagePath(item, 'heart')" alt="" /></div>
<div class="leafs" :style="getAnimationStyle(item)">
<div class="leaf_1"><img :src="getSafeImagePath(item, 'leaf')" alt="" /></div>
<div class="leaf_2"><img :src="getSafeImagePath(item, 'leaf')" alt="" /></div>
<div class="leaf_3"><img :src="getSafeImagePath(item, 'leaf')" alt="" /></div>
</div>
</div>
</div>
<div class="fanlist-pic">
<img :src="getSafeImagePath(item, 'fan')" alt="" />
</div>
<div class="fanlist-pic" style="margin: 0;">
<img src="~assets/dashboard/fannew/base.png" alt="" />
</div>
</el-col>
</el-row>
<el-row class="fanlist-data">
<div class="fanlist-text">
<span class="content-number">{{ item.attributeMap.iwindspeed }}</span>
<span>m/s</span>
</div>
<div class="fanlist-text">
<span class="content-number">{{ item.attributeMap.igenpower }}</span>
<span>kW</span>
</div>
</el-row>
2024-12-18 13:18:45 +08:00
</div>
<div class="fanlist-bottom">
2024-12-19 16:23:48 +08:00
<!-- <span :style="item.attributeMap.locked == 1 ? 'max-width:120px;' : 'max-width:150px;'">
2024-12-20 09:14:03 +08:00
{{ item.attributeMap.firsttriggeredcode }}
2024-12-19 16:23:48 +08:00
</span>-->
2024-12-20 09:14:03 +08:00
<span :style="item.attributeMap.locked == 1 ? 'max-width:120px;' : 'max-width:150px;'">
{{ getFaultDescription(item) }}
</span>
2024-12-19 15:05:36 +08:00
<!-- <el-tag class="tag-panel is-danger">已锁定</el-tag>-->
2024-12-18 13:18:45 +08:00
<el-tag v-if="item.attributeMap.locked === 1" class="tag-panel is-danger">已锁定</el-tag>
</div>
</div>
</div>
2024-12-19 15:05:36 +08:00
<!-- </el-col>-->
</div>
2024-12-18 13:18:45 +08:00
<ContextMenu :pos="contextMenuPos" v-model:visible="OperateVisible">
<template #default>
<div class="modelOperate">
<el-button
class="control-btn"
type="primary"
@click="sendCommand('setTurbineFastStart')"
v-if="realTimeData.processedoperationmode !== 16"
>启动</el-button>
<el-button @click="sendCommand('setTurbineStop')" v-else class="control-btn" type="primary">停机</el-button>
<el-button @click="sendCommand('setTurbineResetStatusCode')" class="control-btn" type="primary">复位</el-button>
<el-button
@click="sendManualCommand(1)"
v-if="realTimeData.locked !== 1"
class="control-btn"
type="primary">锁定</el-button>
<el-button @click="sendManualCommand(0)" v-else class="control-btn" type="primary">解锁</el-button>
<el-button
class="control-btn"
type="primary"
@click="editstandard(1)"
v-if="realTimeData.standard == 0"
>标杆设置</el-button>
<el-button
class="control-btn"
type="primary"
@click="editstandard(0)"
v-else
>标杆取消</el-button>
</div>
</template>
</ContextMenu>
</div>
</template>
<script setup lang="ts">
2024-12-19 15:05:36 +08:00
import {defineProps, ref,onMounted} from 'vue'
2024-12-18 13:18:45 +08:00
import { useRouter } from 'vue-router'
import { adminBaseRoutePath } from '/@/router/static/adminBase'
import ContextMenu from '/@/views/backend/auth/model/contextMenu.vue'
import { sendCommandReq, sendManualCommandReq } from '/@/api/backend/control/request'
import {ElMessage, ElMessageBox} from "element-plus";
import {equipUpdate} from '/@/api/backend/index.ts'
2024-12-19 15:05:36 +08:00
import {queryfaultCodeDict} from "/@/api/backend/theoreticalpowerCurve/request";
2024-12-19 16:23:48 +08:00
import { malFunctionKeys } from '/@/views/backend/equipment/airBlower/utils'
import { useEnumStore } from '/@/stores/enums'
const enumStore = useEnumStore()
2024-12-18 13:18:45 +08:00
const router = useRouter()
const props = defineProps({
parentData: {
type: Array,
default: () => [],
},
})
const getAnimationStyle = (item) => {
const irotorspeed = item.attributeMap?.irotorspeed ?? 0
let animationDuration;
animationDuration = 60 / irotorspeed / 3
const processedoperationmode = item.attributeMap?.processedoperationmode ?? 0
if(processedoperationmode==33){
return {
'animation-duration': `0s`,
'animation-timing-function': 'linear',
'animation-iteration-count': 'infinite',
'animation-direction': 'normaL',
}
}else{
2024-12-19 15:05:36 +08:00
2024-12-18 13:18:45 +08:00
return {
'animation-duration': `${animationDuration}s`,
'animation-timing-function': 'linear',
'animation-iteration-count': 'infinite',
'animation-direction': 'normaL',
}
}
}
const handleClick = (row) => {
if (!router.hasRoute('windTurbine')) {
router.addRoute('admin', {
path: adminBaseRoutePath + '/windTurbine',
name: 'windTurbine',
component: () => import('/@/views/backend/WindBlower/index.vue'),
meta: {
title: '单风机详情',
menuDesc: '单风机详情',
addtab: true,
},
})
}
router.push({
name: 'windTurbine',
query: {
irn: row.irn,
iotModelId: row.modelId,
deviceCode: row.deviceCode,
name: row.name,
model: row.model,
},
})
}
const OperateVisible = ref(false)
const contextMenuPos = ref({
x: 0,
y: 0,
})
const realTimeData = ref<any>({
processedoperationmode: 1111,
locked: 0,
deviceId: '',
name:'',
code:'',
standard:'',
iotModelId:''
})
const windContextMenu = (event: any,curnodeData) => {
contextMenuPos.value.x = event.pageX
contextMenuPos.value.y = event.pageY
realTimeData.value.processedoperationmode=curnodeData.attributeMap.processedoperationmode
realTimeData.value.locked=curnodeData.attributeMap.locked
realTimeData.value.deviceId=curnodeData.irn
realTimeData.value.name=curnodeData.name
realTimeData.value.code=curnodeData.deviceCode
realTimeData.value.standard=curnodeData.standard
realTimeData.value.iotModelId=curnodeData.modelId
OperateVisible.value = true
}
const sendCommand = (type: 'setTurbineFastStart' | 'setTurbineStop' | 'setTurbineResetStatusCode') => {
const sendTypeEnum = {
setTurbineFastStart: '风机快速启动指令',
setTurbineStop: '风机停机指令',
setTurbineResetStatusCode: '风机复位故障代码指令',
}
ElMessageBox.confirm('确认发送' + sendTypeEnum[type] + '吗?', '', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
const serviceName = sendTypeEnum[type]
const optDesc = serviceName + ',设定值为1'
sendCommandReq({
deviceId: realTimeData.value.deviceId as string,
serviceCode: type,
serviceName,
optDesc,
opValue: 1,
}).then((res) => {
if (res.code == 200) {
ElMessage.success('指令发送成功')
} else {
ElMessage.error('指令发送失败')
}
})
})
}
const sendManualCommand = (type: 1 | 0) => {
const serviceName = type === 0 ? '风机解锁' : '风机锁定'
ElMessageBox.confirm('确认' + serviceName + '吗?', '', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
sendManualCommandReq({
deviceId: realTimeData.value.deviceId as string,
serviceCode: 'Locked',
serviceName,
optDesc: serviceName + ',设定值为:' + type,
opValue: type,
}).then((res) => {
if (res.code == 200) {
ElMessage.success('指令发送成功')
} else {
ElMessage.error('指令发送失败')
}
})
})
}
const editstandard = (type: 1 | 0) => {
const standardName = type === 0 ? '取消风机标杆' : '设置风机标杆'
ElMessageBox.confirm('确认' + standardName + '吗?', '', {
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
}).then(() => {
equipUpdate({
id: realTimeData.value.deviceId,
code: realTimeData.value.code,
name:realTimeData.value.name,
iotModelId: realTimeData.value.iotModelId,
standard: type
}).then((res) => {
if (res.code == 200) {
ElMessage.success('设置成功')
} else {
ElMessage.error('设置失败')
}
})
})
}
const getStyles=(color) =>{
if (color && /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/.test(color)) {
const rgbaColor = hexToRgba(color, 0.2);
return {
background: rgbaColor,
2024-12-19 15:05:36 +08:00
border: `1px solid ${color}`,
2024-12-18 13:18:45 +08:00
color: color,
};
} else {
return {
background: 'rgba(48, 89, 236, 0.2)',
border: '1px solid #3059ec',
color: '#3059ec'
};
}
}
const hexToRgba = (hex, alpha) => {
hex = hex.replace('#', '');
let r, g, b;
if (hex.length === 3) {
r = parseInt(hex[0] + hex[0], 16);
g = parseInt(hex[1] + hex[1], 16);
b = parseInt(hex[2] + hex[2], 16);
} else {
r = parseInt(hex.substring(0, 2), 16);
g = parseInt(hex.substring(2, 4), 16);
b = parseInt(hex.substring(4, 6), 16);
}
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
}
2024-12-19 15:05:36 +08:00
const imagePathMap = {
'#9C27B0': {
heart: 'heart1.png',
leaf: 'leaf1.png',
fan: 'fan1.png',
},
'#673AB7': {
heart: 'heart2.png',
leaf: 'leaf2.png',
fan: 'fan2.png',
},
'#3F51B5': {
heart: 'heart3.png',
leaf: 'leaf3.png',
fan: 'fan3.png',
},
'#3059EC': {
heart: 'heart4.png',
leaf: 'leaf4.png',
fan: 'fan4.png',
},
'#0277B3': {
heart: 'heart5.png',
leaf: 'leaf5.png',
fan: 'fan5.png',
},
'#00A096': {
heart: 'heart6.png',
leaf: 'leaf6.png',
fan: 'fan6.png',
},
'#06B429': {
heart: 'heart7.png',
leaf: 'leaf7.png',
fan: 'fan7.png',
},
'#64DD17': {
heart: 'heart8.png',
leaf: 'leaf8.png',
fan: 'fan8.png',
},
'#EEFF41': {
heart: 'heart9.png',
leaf: 'leaf9.png',
fan: 'fan9.png',
},
'#FFB600': {
heart: 'heart10.png',
leaf: 'leaf10.png',
fan: 'fan10.png',
},
'#FF7E00': {
heart: 'heart11.png',
leaf: 'leaf11.png',
fan: 'fan11.png',
},
'#FE3731': {
heart: 'heart12.png',
leaf: 'leaf12.png',
fan: 'fan12.png',
},
'#999999': {
heart: 'heart13.png',
leaf: 'leaf13.png',
fan: 'fan13.png',
}
};
const getImagePath = (item, type) => {
const color = item.attributeMap.color;
return imagePathMap[color]?.[type];
};
const getSafeImagePath = (item, type) => {
const path = getImagePath(item, type);
if (!getSafeImagePath.cache) {
getSafeImagePath.cache = {};
}
if (getSafeImagePath.cache[path]) {
return getSafeImagePath.cache[path];
}
const imagePath = new URL(`/src/assets/dashboard/fannew/${path}`, import.meta.url).href;
getSafeImagePath.cache[path] = imagePath;
return imagePath;
};
2024-12-20 09:14:03 +08:00
const getFaultDescription=(item)=>{
//getMalfunctionEnums(item)
fetchData(item)
2024-12-19 16:23:48 +08:00
let firsttriggeredcode=item.attributeMap.firsttriggeredcode
if (malFunctionKeys.includes('FirstTriggeredCode')) {
firsttriggeredcode = malFunctionEnums?.[firsttriggeredcode] ?? firsttriggeredcode
}
return firsttriggeredcode
2024-12-19 17:10:23 +08:00
}
2024-12-19 15:05:36 +08:00
2024-12-19 17:10:23 +08:00
let malFunctionEnums: any = {}
2024-12-20 09:14:03 +08:00
/*const getMalfunctionEnums = (item) => {
2024-12-19 16:23:48 +08:00
/!*queryfaultCodeDict({ madeinfactory: item!.madeinFactory, model: item!.model }).then((res) => {*!/
queryfaultCodeDict({ madeinfactory: '广东明阳风电', model: 'MY1.5/89' }).then((res) => {
2024-12-19 15:05:36 +08:00
if (res.code == 200) {
const data: any = {}
res.data.forEach((item: any) => {
data[item.code] = item.description
})
malFunctionEnums = data
2024-12-19 16:23:48 +08:00
} else {
console.warn('查询故障代码字典失败:', res.message);
}
2024-12-19 15:05:36 +08:00
})
2024-12-19 16:23:48 +08:00
}*/
2024-12-20 09:14:03 +08:00
const requestedParams = new Set<string>();
const fetchData = async (item: any) => {
const key = `${item.madeinFactory}-${item.model}`;
if (requestedParams.has(key)) {
console.log('Duplicate request detected, skipping...');
return;
}
requestedParams.add(key);
try {
malFunctionEnums={}
const response = await queryfaultCodeDict({ madeinfactory: item.madeinFactory, model: item.model });
if (response.code === 200) {
const data: any = {};
response.data.forEach((item: any) => {
data[item.code] = item.description;
});
malFunctionEnums = data;
} else {
console.warn('查询故障代码字典失败:', response.message);
}
} catch (error) {
console.error('请求出错:', error);
}
};
2024-12-18 13:18:45 +08:00
</script>
<style scoped lang="scss">
@keyframes leafRotate {
0% {
transform: rotate(0deg);
}
25% {
transform: rotate(90deg);
}
50% {
transform: rotate(180deg);
}
75% {
transform: rotate(270deg);
}
100% {
transform: rotate(360deg);
}
}
.wind-mark {
background-image: linear-gradient(140deg, #ffe3e3 0%, #ffffff 93%);
border: 1px solid #ffe3e3;
}
.wind-default {
background-image: linear-gradient(180deg, #f0f6ff 0%, #ffffff 50%);
border: 1px solid #e1edf6;
}
.wind-offline {
background-image: linear-gradient(180deg, #dddddd 0%, #ffffff 93%);
border: 1px solid #eeeeee;
}
.wind-picture {
display: none;
}
.wind-mark-icon {
background: #ffffff;
border: 1px solid #ffe3e3;
border-radius: 8px 0 100px 0;
display: flex;
justify-content: center;
align-items: center;
width: 40px;
height: 30px;
vertical-align: middle;
}
2024-12-19 15:05:36 +08:00
.FanList-content.flex-start {
justify-content: flex-start;
}
2024-12-18 13:18:45 +08:00
.FanList-content {
overflow-x: hidden;
2024-12-19 15:05:36 +08:00
display: grid;
justify-content: space-between;
grid-template-columns: repeat(auto-fill, 170px);
.overviewPart{
display: flex;
box-sizing: border-box;
width: 170px;
height: 110px;
margin-top: 8px;
margin-bottom: 8px;
.FanList-panel {
border-radius: 8px;
cursor: pointer;
.fanlist-top {
display: flex;
width: 100%;
}
.fanlist-icon {
2024-12-18 13:18:45 +08:00
display: flex;
justify-content: center;
align-items: center;
2024-12-19 15:05:36 +08:00
width: 40px;
height: 30px;
vertical-align: middle;
}
.fanlist-name {
width: 100%;
margin-top: 5px;
}
.tag-panel {
border-radius: 0 8px 0 0;
line-height: 20px;
:deep(.el-tag__content){
max-width: 80px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
.is-primary {
background: rgba(2, 119, 179, 0.2);
border: 1px solid #0277b3;
color: #0277b3;
}
.is-success {
background: rgba(6, 180, 41, 0.2);
border: 1px solid #06b429;
color: #06b429;
}
.is-info {
background: rgba(48, 89, 236, 0.2);
border: 1px solid #3059ec;
color: #3059ec;
}
.is-warning {
background: rgba(255, 126, 0, 0.2);
border: 1px solid #ff7e00;
color: #ff7e00;
}
.is-danger {
background: rgba(254, 55, 49, 0.2);
border: 1px solid #fe3731;
color: #fe3731;
}
.is-offline {
background: rgba(153, 153, 153, 0.2);
border: 1px solid #999999;
color: #999999;
}
.is-maintenance {
background: rgba(0, 160, 150, 0.2);
border: 1px solid #00a096;
color: #00a096;
}
.fanlist-main {
width: 100%;
display: flex;
justify-content: space-between;
padding: 5px 10px 0 10px;
text-align: center;
.fanlist-pic {
display: flex;
justify-content: center;
align-items: center;
margin-top: -10px;
.mask {
width: 43px;
height: 38px;
.heart {
2024-12-18 13:18:45 +08:00
position: absolute;
2024-12-19 15:05:36 +08:00
text-align: center;
top: 5px;
left: 19px;
2024-12-18 13:18:45 +08:00
}
2024-12-19 15:05:36 +08:00
.leafs {
2024-12-18 13:18:45 +08:00
position: absolute;
2024-12-19 15:05:36 +08:00
transform-origin: center center;
width: 44px;
height: 48px;
animation-name: leafRotate;
.leaf_1 {
width: 5px;
height: 14px;
position: absolute;
left: 19px;
top: 6px;
}
.leaf_2 {
width: 5px;
height: 14px;
position: absolute;
left: 30px;
top: 22px;
transform: rotate(120deg);
}
.leaf_3 {
width: 5px;
height: 14px;
position: absolute;
left: 9px;
top: 22px;
transform: rotate(240deg);
}
2024-12-18 13:18:45 +08:00
}
}
}
2024-12-19 15:05:36 +08:00
.fanlist-text {
margin-top: 5px;
width: 100%;
text-align: right;
.content-number {
color: #333333;
font-size: 20px;
padding-right: 5px;
}
}
.fanlist-text span {
display: inline-block;
2024-12-18 13:18:45 +08:00
}
}
2024-12-19 15:05:36 +08:00
.fanlist-bottom {
display: flex;
justify-content: space-between;
height: 24px;
span{
display: inline-block;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: #FE3731;
padding-left:5px;
}
.tag-panel {
border-radius: 0 0 8px 0;
line-height: 20px;
}
2024-12-18 13:18:45 +08:00
}
}
}
2024-12-19 15:05:36 +08:00
2024-12-18 13:18:45 +08:00
.modelOperate{
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
width: 80px;
/* height: 80px;*/
.el-button {
margin: 5px;
}
}
}
2024-12-19 15:05:36 +08:00
/*
.FanList-content::after {
content: '';
flex: auto;
!* 或者flex: 1 *!
}
*/
2024-12-18 13:18:45 +08:00
@media screen and (max-width: 1680px) {
.FanList-content {
.FanList-panel {
.fanlist-text {
font-size: 12px !important;
.content-number {
font-size: 16px !important;
}
}
}
}
}
@media screen and (max-width: 1280px) {
.FanList-content {
.FanList-panel {
.fanlist-text {
margin-top: 10px !important;
}
}
}
}
</style>