Files
SaaS-lib/hx-ai-intelligent/src/view/equipmentControl/ventilationSystem/index.vue
2024-08-30 14:03:31 +08:00

729 lines
22 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="box-ventilationSystem">
<div class="legend-box">
<template v-for="(item, index) in legend" :key="index">
<div class="legend-box-item" @click="selectLegend(item, index)">
<div
class="legend-box-item-img"
:style="{
'background-image': selectIndex === index ? 'url(' + selectImg + ')' : '',
}">
<img :src="item.url" />
</div>
<div class="legend-box-item-name">
{{ item.name }}
</div>
</div>
</template>
</div>
<!-- 温度 -->
<transition name="zep">
<div v-if="selectIndex === 0">
<template v-for="(item, index) in sensorData" :key="index">
<div
style="position: absolute"
:style="{ left: item.styleText.left, bottom: item.styleText.bottom }">
<singleModel :dataSource="item" />
</div>
</template>
</div>
</transition>
<!-- 湿度 -->
<transition name="zep">
<div v-if="selectIndex === 1">
<template v-for="(item, index) in humidityData" :key="index">
<div
style="position: absolute"
:style="{ left: item.styleText.left, bottom: item.styleText.bottom }">
<singleModel :dataSource="item" />
</div>
</template>
</div>
</transition>
<!-- PM2.5 -->
<transition name="zep">
<div v-if="selectIndex === 2">
<template v-for="(item, index) in pmData" :key="index">
<div
style="position: absolute"
:style="{ left: item.styleText.left, bottom: item.styleText.bottom }">
<singleModel :dataSource="item" />
</div>
</template>
</div>
</transition>
<!-- CO2e -->
<transition name="zep">
<div v-if="selectIndex === 3">
<template v-for="(item, index) in CO2Data" :key="index">
<div
style="position: absolute"
:style="{ left: item.styleText.left, bottom: item.styleText.bottom }">
<singleModel :dataSource="item" />
</div>
</template>
</div>
</transition>
<!-- 右侧抽屉的触发按钮 -->
<div
class="drawer-box-in"
v-if="!visible && (selectIndex === 4 || selectIndex === 5 || selectIndex === 6)"
@click="toggleDrawer">
<double-left-outlined class="drawer-icon" style="color: white" />
</div>
<!-- 排风扇 -->
<transition name="zep">
<div v-if="selectIndex === 4">
<template v-for="(item, index) in fanData" :key="index">
<!-- 朝左边 -->
<template v-if="item.lineType === '1'">
<div
style="position: absolute"
:style="{
left: 'calc(' + item.styleText.left + ' - 12.6%)',
bottom: 'calc(' + item.styleText.bottom + ' + 4%)',
}">
<boxModel :dataSource="item" />
</div>
<img
style="width: 50px; height: 75px; position: absolute; z-index: 1"
:style="{
left: 'calc(' + item.styleText.left + ' - 2%)',
bottom: 'calc(' + item.styleText.bottom + ' + 3%)',
transform: 'rotateY(180deg)',
}"
src="../image/liftState/lift/line.png" />
</template>
<!-- 朝右边 -->
<template v-if="item.lineType === '2'">
<div
style="position: absolute"
:style="{
left: 'calc(' + item.styleText.left + ' + 4.3%)',
bottom: 'calc(' + item.styleText.bottom + ' + 4%)',
}">
<boxModel :dataSource="item" />
</div>
<img
style="width: 50px; height: 75px; position: absolute; z-index: 1"
:style="{
left: 'calc(' + item.styleText.left + ' + 1.5%)',
bottom: 'calc(' + item.styleText.bottom + ' + 3%)',
}"
src="../image/liftState/lift/line.png" />
</template>
<img
style="width: 38px; height: 38px; position: absolute; cursor: pointer; z-index: 9"
:style="{ left: item.styleText.left, bottom: item.styleText.bottom }"
src="../image/ventilationSystem/fan.png" />
</template>
</div>
</transition>
<!-- 排风扇 - 弹窗 -->
<a-drawer
v-if="selectIndex === 4 && visible"
:visible="selectIndex === 4"
class="drawer-fan drawer-item"
:width="496"
:forceRender="preload"
placement="right"
:body-style="{ background: 'rgba(0, 0, 0)', opacity: 0.8, color: 'white' }"
:closable="false"
id="drawer"
:maskStyle="{ 'background-color': 'rgba(0, 0, 0, 0)' }"
:mask="false">
<!-- 左侧抽屉的关闭按钮 -->
<div class="drawer-box-out" @click="toggleDrawer">
<double-right-outlined class="drawer-icon" style="color: white" />
</div>
<a-tabs v-model:activeKey="activeKey">
<a-tab-pane key="1" tab="控制面板">
<fanControl
ref="tabs1Ref"
@reset="reset"
@reset-all="resetDrawer"
:treeData="treeData"
:type="`排风扇`" />
</a-tab-pane>
<a-tab-pane key="2" tab="计划列表" force-render>
<fanPlant ref="tabs2Ref" @reset-all="resetDrawer" :status="stateList" :type="3" />
</a-tab-pane>
<a-tab-pane key="3" tab="日志">
<fanLog ref="tabs3Ref" @reset-all="resetDrawer" :type="3" />
</a-tab-pane>
</a-tabs>
</a-drawer>
<!-- 风幕机 -->
<transition name="zep">
<div v-if="selectIndex === 5">
<template v-for="(item, index) in airCurtainData" :key="index">
<div
style="position: absolute"
:style="{
left: 'calc(' + item.styleText.left + ' - 16.1%)',
bottom: 'calc(' + item.styleText.bottom + ' + 8%)',
}">
<boxModel :dataSource="item" />
</div>
<img
style="width: 50px; height: 75px; position: absolute; z-index: 1"
:style="{
left: 'calc(' + item.styleText.left + ' - 2%)',
bottom: 'calc(' + item.styleText.bottom + ' + 4%)',
transform: 'rotateY(180deg)',
}"
src="../image/liftState/lift/line.png" />
<img
style="width: 38px; height: 38px; position: absolute; z-index: 9"
:style="{ left: item.styleText.left, bottom: item.styleText.bottom }"
src="../image/ventilationSystem/airCurtain.png" />
</template>
</div>
</transition>
<!-- 风幕机 - 弹窗 -->
<a-drawer
v-if="selectIndex === 5 && visible"
:visible="selectIndex === 5"
class="drawer-fan drawer-item"
:width="496"
:forceRender="preload"
placement="right"
:body-style="{ background: 'rgba(0, 0, 0)', opacity: 0.8, color: 'white' }"
:closable="false"
id="drawer"
:maskStyle="{ 'background-color': 'rgba(0, 0, 0, 0)' }"
:mask="false">
<!-- 左侧抽屉的关闭按钮 -->
<div class="drawer-box-out" @click="toggleDrawer">
<double-right-outlined class="drawer-icon" style="color: white" />
</div>
<a-tabs v-model:activeKey="activeKey">
<a-tab-pane key="1" tab="控制面板">
<fanControl
ref="tabs1Ref"
@reset="reset"
@reset-all="resetDrawer"
:treeData="treeData"
:type="`风幕机`" />
</a-tab-pane>
<a-tab-pane key="2" tab="计划列表" force-render>
<fanPlant ref="tabs2Ref" @reset-all="resetDrawer" :status="stateList" :type="4" />
</a-tab-pane>
<a-tab-pane key="3" tab="日志">
<fanLog ref="tabs3Ref" @reset-all="resetDrawer" :type="4" />
</a-tab-pane>
</a-tabs>
</a-drawer>
<transition name="zep">
<div v-if="selectIndex === 6">
<template v-for="(item, index) in windowData" :key="index">
<div
style="position: absolute"
:style="{
left: 'calc(' + item.styleText.left + ' - 11%)',
bottom: 'calc(' + item.styleText.bottom + ' + 8%)',
}">
<boxModel :dataSource="item" />
</div>
<img
style="width: 50px; height: 75px; position: absolute; z-index: 1"
:style="{
left: 'calc(' + item.styleText.left + ' - 2%)',
bottom: 'calc(' + item.styleText.bottom + ' + 4%)',
transform: 'rotateY(180deg)',
}"
src="../image/liftState/lift/line.png" />
<img
style="width: 38px; height: 38px; position: absolute; z-index: 9"
:style="{ left: item.styleText.left, bottom: item.styleText.bottom }"
src="../image/ventilationSystem/windowIcon.png" />
</template>
</div>
</transition>
<!-- 电动窗 - 弹窗 -->
<a-drawer
v-if="selectIndex === 6 && visible"
:visible="selectIndex === 6"
class="drawer-fan drawer-item"
:width="496"
:forceRender="preload"
placement="right"
:body-style="{ background: 'rgba(0, 0, 0)', opacity: 0.8, color: 'white' }"
:closable="false"
id="drawer"
:maskStyle="{ 'background-color': 'rgba(0, 0, 0, 0)' }"
:mask="false">
<!-- 左侧抽屉的关闭按钮 -->
<div class="drawer-box-out" @click="toggleDrawer">
<double-right-outlined class="drawer-icon" style="color: white" />
</div>
<a-tabs v-model:activeKey="activeKey">
<a-tab-pane key="1" tab="控制面板">
<fanControl
ref="tabs1Ref"
@reset="reset"
@reset-all="resetDrawer"
:treeData="treeData"
:type="`电动窗`" />
</a-tab-pane>
<a-tab-pane key="2" tab="计划列表" force-render>
<fanPlant ref="tabs2Ref" @reset-all="resetDrawer" :status="stateList" :type="5" />
</a-tab-pane>
<a-tab-pane key="3" tab="日志">
<fanLog ref="tabs3Ref" @reset-all="resetDrawer" :type="5" />
</a-tab-pane>
</a-tabs>
</a-drawer>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted, onUnmounted } from 'vue';
//图片
import temperature from '../image/airConditioningSystem/temperature.svg';
import humidity from '../image/ventilationSystem/humidity.svg';
import PM25 from '../image/ventilationSystem/PM25.svg';
import CO2 from '../image/ventilationSystem/CO2.svg';
import ventilatingFan from '../image/ventilationSystem/ventilatingFan.svg';
import window from '../image/ventilationSystem/window.svg';
import airCurtain from '../image/ventilationSystem/fanMachine.svg';
import selectImg from '../image/airConditioningSystem/selectImg.png';
import sunRed from '../image/airConditioningSystem/sunRed.png';
import sunYellow from '../image/airConditioningSystem/sunYellow.png';
import sunGreen from '../image/airConditioningSystem/sunGreen.png';
import boxModel from './components/boxModel.vue';
import singleModel from '../components/singleModel.vue';
import fanControl from './components/fanControl.vue';
import fanPlant from './components/fanPlant.vue';
import fanLog from './components/fanLog.vue';
import { DoubleLeftOutlined, DoubleRightOutlined } from '@ant-design/icons-vue';
// 请求
import { http } from '/nerv-lib/util/http';
import { getEnum } from '/@/api';
import { ventilating } from '/@/api/ventilatingSystem';
// 全局变量
import { items } from '/@/store/item';
// 定位数据
import {
devicePosition1,
devicePosition2,
devicePosition3,
devicePosition,
} from './devicePosition';
// 父子组件交互 =======================================================================
// 全局变量
const state = items();
// 状态枚举
const stateList = ref([]);
// 获得状态枚举
const getStateEnum = async () => {
let enumData = await getEnum({ params: { enumType: 'PlanExecuteStatus' } });
stateList.value = enumData.data;
};
// 左侧侧边栏及交互 ====================================================================
// 图例
const legend = ref([
{ url: temperature, name: '温度' },
{ url: humidity, name: '湿度' },
{ url: PM25, name: 'PM2.5' },
{ url: CO2, name: 'CO2浓度' },
{ url: ventilatingFan, name: '排风扇' },
{ url: airCurtain, name: '风幕机' },
{ url: window, name: '电动窗' },
]);
// 选择的图例
const selectIndex = ref(0);
// 图例切换事件
const selectLegend = (item: any, index: any) => {
const name = item.name;
if (selectIndex.value === index) {
return;
} else if (name == '排风扇') {
// 获得树形结构
getTree(1, index);
// 获得设备图例
getDevice(1);
} else if (name == '风幕机') {
getTree(2, index);
getDevice(2);
} else if (name == '电动窗') {
getTree(3, index);
getDevice(3);
// 修改
} else {
selectIndex.value = index;
}
};
// 抽屉相关(排风扇 / 风幕机 / 电动窗) ==================================================
// 选择table
const activeKey = ref('1');
// 抽屉的展示状态
const visible = ref(true);
// 预加载flag获得分区数据后预加载抽屉防止获取ref报错
const preload = ref(true);
// 抽屉 - 开关事件
const toggleDrawer = () => {
visible.value = !visible.value;
};
// 当前设备的请求URL
let url = '';
// 刷新当前的树形结构数据
const reload = () => {
http.get(url, { projectId: state.projectId, siteId: state.siteId }).then((res) => {
const data = res.data[0].childList;
// 默认选中第一个分区
data.forEach((item: any, index: number) => {
if (index === 0) {
item.selected = true;
} else {
item.selected = false;
}
});
treeData.value = data;
// 楼层信息
// selectIndex.value = index;
// 反向刷新
try {
tabs1Ref.value.setButtons2(treeData.value[0].childList);
} catch {}
});
};
// 抽屉中tab1 - 控制面板 =================================================================
const treeData = ref([]);
// 获取tab1的树形结构
const getTree = async (type: number, index: number) => {
// 切换时默认选择tab1
activeKey.value = '1';
if (type == 1) {
url = ventilating.getTree1;
} else if (type == 2) {
url = ventilating.getTree2;
} else {
url = ventilating.getTree3;
}
state.setLoading(true);
http
.get(url, { projectId: state.projectId, siteId: state.siteId })
.then((res) => {
state.setLoading(false);
const data = res.data[0].childList;
// 默认选中第一个分区
data.forEach((item: any, index: number) => {
if (index === 0) {
item.selected = true;
} else {
item.selected = false;
}
});
treeData.value = data;
selectIndex.value = index;
})
.catch(() => {
state.setLoading(false);
});
};
const getDevice = async (type: number) => {
// 切换时默认选择tab1
activeKey.value = '1';
let arr: Array<Object> = [];
let url = '';
let deviceType = '';
if (type == 1) {
// 设备类型
deviceType = 'fan';
// 位置数据
arr = devicePosition1;
// 请求地址
url = ventilating.getDevice1;
} else if (type == 2) {
deviceType = 'airCurtain';
arr = devicePosition2;
url = ventilating.getDevice2;
} else if (type == 3) {
deviceType = 'window';
arr = devicePosition3;
url = ventilating.getDevice3;
}
http.get(url, { projectId: state.projectId, siteId: state.siteId, floor: 1 }).then((res) => {
const data = res.data;
data.forEach((item: any, index: number) => {
let obj = arr[index];
item.styleText = obj.textObject;
item.lineType = obj.lineType;
item.type = deviceType;
});
if (type == 1) {
fanData.value = data;
} else if (type == 2) {
airCurtainData.value = data;
} else if (type == 3) {
windowData.value = data;
}
});
};
// 重置分区树所有当前选项
const reset = () => {
treeData.value.forEach((item: any) => {
item.selected = false;
item.childList.forEach((i: any) => {
i.selected = false;
});
});
};
// 温度数组
const sensorData = ref<any>([]);
// 湿度数组
const humidityData = ref<any>([]);
// PM2.5数组
const pmData = ref<any>([]);
// 二氧化碳 浓度
const CO2Data = ref<any>([]);
// 排风扇
const fanData = ref([]);
// 风幕机
const airCurtainData = ref([]);
// 电动窗
const windowData = ref([]);
// 当其中一个tab产生了数据修改可以调用该方法重置所有tab
const resetDrawer = () => {
// tab1重置
reload();
if (selectIndex.value == 4) {
getDevice(1);
} else if (selectIndex.value == 5) {
getDevice(2);
} else if (selectIndex.value == 6) {
getDevice(3);
}
tabs1Ref.value.refresh();
// tab2 tab3 可能未加载
try {
// tab2重置
tabs2Ref.value.reset();
} catch {}
try {
// tab3重置
tabs3Ref.value.reset();
} catch {}
};
// 抽屉tab1组件的引用
const tabs1Ref = ref();
// 抽屉tab2组件的引用
const tabs2Ref = ref();
// 抽屉tab3组件的引用
const tabs3Ref = ref();
//根据温度获取图片
const getSunUrl = (number: any) => {
if (number > 0 && number < 20) {
return sunGreen;
} else if (number >= 20 && number < 30) {
return sunYellow;
} else {
return sunRed;
}
};
//获取温度颜色
const getColor = (number: any) => {
if (number > 0 && number < 20) {
return '#0dffa4';
} else if (number >= 20 && number < 30) {
return '#f59a23';
} else {
return '#f36163';
}
};
// 获取传感器数据
const getSensorData = () => {
sensorData.value = [];
humidityData.value = [];
pmData.value = [];
CO2Data.value = [];
http
.get(ventilating.getSensorData, { projectId: state.projectId, siteId: state.siteId })
.then((res) => {
if (res.msg === 'success') {
res.data.forEach((item: any, index: number) => {
sensorData.value.push({
title: item.deviceInfoName,
styleText: devicePosition[index].styleText,
type: '温度',
unit: item.record.tempUnit,
number: item.record.temp,
url: getSunUrl(item.record.temp),
fontColor: getColor(item.record.temp),
});
humidityData.value.push({
title: item.deviceInfoName,
styleText: devicePosition[index].styleText,
type: '湿度',
unit: item.record.humidityUnit,
number: item.record.humidity,
url: humidity,
});
pmData.value.push({
title: item.deviceInfoName,
styleText: devicePosition[index].styleText,
type: 'pm2.5',
unit: item.record.pm2_5Unit,
number: item.record.pm2_5,
url: PM25,
});
CO2Data.value.push({
title: item.deviceInfoName,
styleText: devicePosition[index].styleText,
type: '二氧化碳',
unit: item.record.co_CO2Unit,
number: item.record.co_CO2,
url: CO2,
});
});
}
});
};
//实时获取数据 暂定一分钟
const intervalId = setInterval(getSensorData, 60000);
onMounted(() => {
//获取传感器数据
getSensorData();
// 获得枚举
getStateEnum();
});
onUnmounted(() => {
clearInterval(intervalId);
});
</script>
<style lang="less">
@import url('../style/color.less');
.legend-box {
width: 80px;
height: 100%;
background-color: rgba(33, 40, 54, 0.95);
display: flex;
flex-direction: column;
gap: 10px;
padding: 15px 5px;
.legend-box-item {
display: flex;
flex-direction: column;
align-items: center;
width: 70px;
height: 70px;
.legend-box-item-img {
width: 50px;
height: 60px;
padding-top: 5px;
flex: 1;
cursor: pointer;
background-size: 100% 100%;
text-align: center;
img {
transition: all ease 0.2s;
}
img:hover {
transform: scale(1.1);
}
}
.legend-box-item-name {
transform: translateY(-5px);
text-align: center;
color: white;
font-size: 13px;
}
}
}
.box-ventilationSystem {
width: 100%;
height: 100%;
display: flex;
overflow: hidden;
background-image: url('../image/ventilationSystem/ventilationSystemBg.png');
background-size: 100% 99.9%;
background-repeat: no-repeat;
position: relative;
}
.zep-enter-active,
.zep-leave-active {
animation-duration: 0.6s; /* 增加动画持续时间 */
animation-timing-function: ease-in-out; /* 使用更平滑的动画曲线 */
}
.zep-enter-active {
animation-name: bounce-enter;
}
.zep-leave-active {
animation-name: bounce-leave;
}
/* 进入动画 */
@keyframes bounce-enter {
0% {
opacity: 0; /* 初始时完全透明 */
}
100% {
opacity: 1; /* 结束时完全不透明 */
}
}
/* 离开动画 */
@keyframes bounce-leave {
0% {
opacity: 1; /* 初始时完全不透明 */
}
100% {
opacity: 0; /* 结束时完全透明 */
}
}
.drawer-fan {
width: 496px;
// 抽屉关闭按钮
.drawer-box-out {
width: 30px;
height: 40px;
border-radius: 2px;
position: fixed;
right: 495px;
top: 0;
bottom: 0;
margin: auto;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
.ant-tabs-tab-btn {
color: white;
}
.ant-tabs-tabpane {
color: white;
}
}
.drawer-box-in {
width: 30px;
height: 40px;
border-radius: 2px;
position: fixed;
right: 0;
top: 0;
bottom: 0;
margin: auto;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
</style>