@ -0,0 +1,10 @@
|
||||
import request from "@/utils/request"; |
||||
|
||||
// 获取设备策略管理列表
|
||||
export function policyListData(query) { |
||||
return request({ |
||||
url: "/policy/list", |
||||
method: "get", |
||||
params: query, |
||||
}); |
||||
} |
After Width: | Height: | Size: 8.1 KiB |
After Width: | Height: | Size: 513 B |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 317 KiB |
After Width: | Height: | Size: 277 KiB |
After Width: | Height: | Size: 205 KiB |
After Width: | Height: | Size: 285 KiB |
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 217 KiB |
After Width: | Height: | Size: 1.6 MiB |
After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 54 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 4.6 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 20 KiB |
After Width: | Height: | Size: 186 KiB |
After Width: | Height: | Size: 5.9 KiB |
After Width: | Height: | Size: 317 B |
After Width: | Height: | Size: 783 B |
@ -0,0 +1,433 @@
|
||||
<template> |
||||
<div class="app-container"> |
||||
<img |
||||
class="right-line" |
||||
src="../../../assets/images/right_line.png" |
||||
alt="" |
||||
/> |
||||
<div class="delay-title">自动开关机时间设置</div> |
||||
<div class="delaySwitch"> |
||||
<div class="delay-li" v-for="(item, index) in delayList" :key="index"> |
||||
<div class="buildingDiv"> |
||||
<img |
||||
class="title-bg" |
||||
src="../../../assets/images/title-bg.png" |
||||
alt="" |
||||
/> |
||||
<div class="title-word">{{ item.name }}:</div> |
||||
</div> |
||||
<div class="delay-bottom"> |
||||
<div class="start"> |
||||
<div>初始化</div> |
||||
<img |
||||
class="start-img" |
||||
src="../../../assets/images/start.png" |
||||
alt="" |
||||
/> |
||||
</div> |
||||
<div class="delay-right"> |
||||
<div |
||||
class="delay-context" |
||||
v-for="(children, subIndex) in item.values" |
||||
:key="subIndex + 1" |
||||
> |
||||
<div class="device-arrow"> |
||||
<div class="delay-input"> |
||||
<el-input |
||||
size="mini" |
||||
v-model="children.curValue" |
||||
@keyup.enter.native="handleEnter(children, $event)" |
||||
@input="handleInput(children)" |
||||
@focus="handleFocus(index, subIndex)" |
||||
@blur="handleBlur()" |
||||
></el-input |
||||
>S |
||||
</div> |
||||
<!-- 将 isHovered 状态传递给 dynamic-arrow 组件 --> |
||||
<dynamic-arrow |
||||
:is-hovered="currentFocusIndex === `${index}-${subIndex}`" |
||||
></dynamic-arrow> |
||||
</div> |
||||
<div class="device-body"> |
||||
<div>{{ children.pointName }}</div> |
||||
<img |
||||
v-if="children.pointName === '冷却泵'" |
||||
src="../../../assets/images/coolingPump.png" |
||||
class="butterflyValve" |
||||
alt="" |
||||
/> |
||||
<img |
||||
v-if="children.pointName === '冷冻泵'" |
||||
src="../../../assets/images/freezingPump.png" |
||||
class="butterflyValve" |
||||
alt="" |
||||
/> |
||||
<img |
||||
v-if="children.pointName.includes('主机')" |
||||
src="../../../assets/images/host.png" |
||||
class="butterflyValve" |
||||
alt="" |
||||
/> |
||||
<img |
||||
v-if="children.pointName.includes('塔')" |
||||
src="../../../assets/images/coolingTower.png" |
||||
class="butterflyValve" |
||||
alt="" |
||||
/> |
||||
<img |
||||
v-if="children.pointName.includes('阀')" |
||||
src="../../../assets/images/butterflyValve.png" |
||||
class="butterflyValve" |
||||
alt="" |
||||
/> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
<img class="left-line" src="../../../assets/images/left_line.png" alt="" /> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import { policyListData } from "@/api/centerairC/strategy"; |
||||
import dynamicArrow from "../../components/dynamicArrow.vue"; |
||||
import { operationConrol } from "@/api/hotWater/waterControl"; |
||||
export default { |
||||
components: { dynamicArrow }, |
||||
data() { |
||||
return { |
||||
delayList: [], |
||||
currentFocusIndex: -1, // 用于记录当前鼠标悬停的索引 |
||||
}; |
||||
}, |
||||
mounted() { |
||||
this.getPolicyList(); |
||||
}, |
||||
methods: { |
||||
// 请求列表 |
||||
getPolicyList() { |
||||
let data = { |
||||
systemType: "0", |
||||
funPolicyType: "2", |
||||
}; |
||||
policyListData(data).then((res) => { |
||||
console.log("设备延时开关返回res", res); |
||||
if (res.code == 200) { |
||||
this.delayList = res.rows; |
||||
} |
||||
}); |
||||
}, |
||||
handleFocus(index, subIndex) { |
||||
console.log("index", index); |
||||
// 当 el-input 获得焦点时,更新 focusedIndex |
||||
this.currentFocusIndex = `${index}-${subIndex}`; |
||||
}, |
||||
// 失去焦点 |
||||
handleBlur() { |
||||
this.currentFocusIndex = ""; |
||||
}, |
||||
// 处理输入事件,过滤非数字字符 |
||||
handleInput(item) { |
||||
console.log("校验"); |
||||
// 实时校验并过滤非数字和非小数点字符 |
||||
item.curValue = String(item.curValue).replace(/[^\d.]/g, ""); |
||||
// 确保只有一个小数点 |
||||
const parts = item.curValue.split("."); |
||||
if (parts.length > 2) { |
||||
// 如果有多个小数点,只保留第一个小数点及之前的部分和第一个小数点之后的部分 |
||||
item.curValue = parts[0] + "." + parts[1]; |
||||
} |
||||
// 确保小数点后最多两位 |
||||
if (parts.length === 2) { |
||||
item.curValue = parts[0] + "." + parts[1].slice(0, 2); |
||||
} |
||||
}, |
||||
// 设置延时时间 |
||||
handleEnter(item, event) { |
||||
console.log("item", item); |
||||
console.log("event", event); |
||||
this.$confirm( |
||||
`确定要修改"${item.pointName}"的延迟时间为:${item.curValue} S吗?`, |
||||
"提示", |
||||
{ |
||||
confirmButtonText: "确定", |
||||
cancelButtonText: "取消", |
||||
type: "warning", |
||||
} |
||||
) |
||||
.then(() => { |
||||
this.hadleOperationConrol(item.cpmId, item.curValue); |
||||
// 失去焦点 |
||||
event.target.blur(); |
||||
this.currentFocusIndex = ""; |
||||
}) |
||||
.catch(() => { |
||||
// // 失去焦点 |
||||
// event.target.blur(); |
||||
// this.currentFocusIndex = ""; |
||||
}); |
||||
}, |
||||
hadleOperationConrol(id, param) { |
||||
let data = { |
||||
id: id, |
||||
param: param, |
||||
}; |
||||
console.log("操作参数", data); |
||||
operationConrol([data]) |
||||
.then((res) => { |
||||
if (res.code == 200) { |
||||
this.$modal.msgSuccess("操作成功"); |
||||
// 更新所有设备状态; |
||||
this.getPolicyList(); |
||||
} else { |
||||
// this.$modal.msgError("操作失败"); |
||||
console.log("应该更新状态的"); |
||||
// 更新所有设备状态; |
||||
this.getPolicyList(); |
||||
} |
||||
}) |
||||
.catch((error) => { |
||||
console.log("请求发生错误,更新设备状态", error); |
||||
// 更新所有设备状态; |
||||
this.getPolicyList(); |
||||
}); |
||||
}, |
||||
}, |
||||
}; |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
.app-container { |
||||
position: relative; |
||||
padding: 0.2rem; |
||||
.delay-title { |
||||
/* 基础样式 */ |
||||
display: inline-block; |
||||
padding: 0.1rem; |
||||
font-family: "Arial Rounded MT Bold", sans-serif; |
||||
font-size: 0.18rem; |
||||
font-weight: 700; |
||||
color: #e6f3ff; |
||||
text-shadow: 0 2px 4px rgba(0, 60, 120, 0.4); |
||||
letter-spacing: 2px; |
||||
position: relative; |
||||
overflow: hidden; |
||||
cursor: default; |
||||
|
||||
/* 双背景叠加 */ |
||||
background: linear-gradient( |
||||
65deg, |
||||
rgba(1, 11, 24, 0.8) 0%, |
||||
rgba(36, 91, 146, 0.6) 50%, |
||||
rgba(18, 53, 95, 0.5) 100% |
||||
), |
||||
linear-gradient(-25deg, #0b274b 20%, #2b4d6b 80%); |
||||
background-blend-mode: overlay; |
||||
|
||||
/* 动态边框 */ |
||||
border: 2px solid; |
||||
border-image: linear-gradient(45deg, #026bbb, transparent) 1; |
||||
|
||||
|
||||
/* 主流光动画 */ |
||||
&::before { |
||||
content: ""; |
||||
position: absolute; |
||||
top: -50%; |
||||
left: -100%; |
||||
width: 250%; |
||||
height: 200%; |
||||
background: linear-gradient( |
||||
60deg, |
||||
transparent 20%, |
||||
rgba(255, 255, 255, 0.3) 50%, |
||||
transparent 80% |
||||
); |
||||
animation: shine 2.5s infinite linear; |
||||
} |
||||
|
||||
/* 副流光动画 */ |
||||
&::after { |
||||
content: ""; |
||||
position: absolute; |
||||
top: -30%; |
||||
left: -50%; |
||||
width: 200%; |
||||
height: 160%; |
||||
background: linear-gradient( |
||||
-30deg, |
||||
transparent 30%, |
||||
rgba(255, 255, 255, 0.2) 50%, |
||||
transparent 70% |
||||
); |
||||
animation: shineReverse 3s infinite linear; |
||||
animation-delay: 0.8s; |
||||
} |
||||
} |
||||
|
||||
/* 主流光动画 */ |
||||
@keyframes shine { |
||||
0% { |
||||
transform: translateX(-20%) rotate(60deg); |
||||
} |
||||
100% { |
||||
transform: translateX(120%) rotate(60deg); |
||||
} |
||||
} |
||||
|
||||
/* 反向副流光 */ |
||||
@keyframes shineReverse { |
||||
0% { |
||||
transform: translateX(20%) rotate(-30deg); |
||||
} |
||||
100% { |
||||
transform: translateX(-120%) rotate(-30deg); |
||||
} |
||||
} |
||||
.right-line { |
||||
position: absolute; |
||||
top: 0; |
||||
left: 0; |
||||
width: 1.7rem; |
||||
height: 0.4rem; |
||||
} |
||||
.left-line { |
||||
position: absolute; |
||||
right: 0; |
||||
bottom: 0; |
||||
width: 0.5rem; |
||||
height: 2.2rem; |
||||
} |
||||
} |
||||
.delaySwitch { |
||||
display: flex; |
||||
flex-direction: column; |
||||
background-color: #142c4e; |
||||
border-radius: 10px; |
||||
padding: 0 20px; |
||||
font-size: 18px; |
||||
color: rgb(166, 217, 252); |
||||
.delay-li { |
||||
display: flex; |
||||
flex-direction: column; |
||||
align-items: flex-start; |
||||
padding: 40px 0; |
||||
border-bottom: 1px dashed #0349ac; |
||||
.buildingDiv { |
||||
padding-left: 54px; |
||||
display: flex; |
||||
flex-direction: row; |
||||
align-items: center; |
||||
margin-bottom: 20px; |
||||
position: relative; |
||||
.title-bg { |
||||
width: 208px; |
||||
height: 38px; |
||||
position: absolute; |
||||
left: 0; |
||||
z-index: 0; |
||||
} |
||||
.title-word { |
||||
z-index: 10; |
||||
font-family: YouSheBiaoTiHei; |
||||
font-size: 24px; |
||||
color: #ffffff; |
||||
white-space: nowrap; |
||||
} |
||||
} |
||||
.delay-bottom { |
||||
display: flex; |
||||
flex-direction: row; |
||||
align-items: flex-start; |
||||
.start { |
||||
margin-right: 20px; |
||||
margin-top: 10px; |
||||
white-space: nowrap; |
||||
display: flex; |
||||
flex-direction: column; |
||||
align-items: center; |
||||
.start-img { |
||||
width: 70px; |
||||
height: 70px; |
||||
margin-top: 10px; |
||||
animation: rotateZ 5s linear infinite; |
||||
transition: 0.6s; /* 设置过渡时间,控制翻转速度 */ |
||||
transform-style: preserve-3d; /* 保持子元素的 3D 效果 */ |
||||
position: relative; |
||||
} |
||||
} |
||||
/* 定义旋转动画 */ |
||||
@keyframes rotateZ { |
||||
0% { |
||||
transform: rotateY(0deg); /* 初始状态,不旋转 */ |
||||
} |
||||
50% { |
||||
transform: rotateY(180deg); /* 旋转到 180 度,呈现翻面效果 */ |
||||
} |
||||
100% { |
||||
transform: rotateY(360deg); /* 旋转回 360 度,回到初始状态 */ |
||||
} |
||||
} |
||||
.delay-right { |
||||
display: flex; |
||||
flex-direction: row; |
||||
align-items: center; |
||||
flex-wrap: wrap; |
||||
} |
||||
.delay-context { |
||||
display: flex; |
||||
flex-direction: row; |
||||
align-items: center; |
||||
margin-bottom: 20px; |
||||
min-width: 150px; |
||||
.device-arrow { |
||||
display: flex; |
||||
flex-direction: column; |
||||
align-items: center; |
||||
} |
||||
.device-body { |
||||
display: flex; |
||||
flex-direction: column; |
||||
align-items: center; |
||||
margin: 0px 30px; |
||||
padding: 10px; |
||||
} |
||||
.butterflyValve { |
||||
width: 70px; |
||||
height: 70px; |
||||
margin-top: 10px; |
||||
padding: 5px; |
||||
border-radius: 5px; |
||||
background-color: #2c4970; |
||||
} |
||||
} |
||||
.delay-input { |
||||
display: flex; |
||||
flex-direction: row; |
||||
align-items: center; |
||||
flex-wrap: nowrap; |
||||
margin-bottom: 10px; |
||||
} |
||||
} |
||||
} |
||||
.delay-li:nth-last-child(1) { |
||||
border-bottom: none; |
||||
} |
||||
} |
||||
</style> |
||||
<style scoped> |
||||
.delay-input >>> .el-input { |
||||
width: 80px !important; |
||||
margin: 0 15px 0 5px; |
||||
} |
||||
.delay-input >>> .el-input__inner { |
||||
background-color: #04193a; |
||||
border: 1px solid #1262db; |
||||
color: #3ef0fd; |
||||
font-weight: 700; |
||||
text-align: center; |
||||
padding: 0 0.1rem !important; |
||||
} |
||||
</style> |
@ -0,0 +1,348 @@
|
||||
<template> |
||||
<div class="app-container policy-flex"> |
||||
<div |
||||
class="policy" |
||||
:class="{ 'radio-style': hasRadioInput(item.values) }" |
||||
v-for="(item, index) in policyList" |
||||
:key="index" |
||||
> |
||||
<div class="policy-title">{{ item.name }}</div> |
||||
<div |
||||
class="policy-li" |
||||
v-for="(children, index) in item.values" |
||||
:key="index + 1" |
||||
> |
||||
<template v-if="children.inputType === 'text'"> |
||||
<div>{{ children.pointName }}:</div> |
||||
<div class="right-data"> |
||||
<el-input |
||||
v-if="children.inputType === 'text'" |
||||
v-model="children.curValue" |
||||
placeholder="请输入" |
||||
size="mini" |
||||
@keyup.enter.native="handleEnter(children)" |
||||
@input="handleInput(children)" |
||||
></el-input> |
||||
<div v-if="children.inputType === 'radio'"> |
||||
<el-radio-group |
||||
v-model="children.curValue" |
||||
@change="handleRadioChange(children, item)" |
||||
> |
||||
<el-radio |
||||
:label="option.pointName" |
||||
v-for="option in item.values" |
||||
:key="option.id" |
||||
>{{ option.pointName }}</el-radio |
||||
> |
||||
</el-radio-group> |
||||
</div> |
||||
<div class="unit">{{ children.unit }}</div> |
||||
</div> |
||||
</template> |
||||
<template v-if="children.inputType === 'radio'"> |
||||
<div class="right-data"> |
||||
<el-radio-group |
||||
v-model="children.curValue" |
||||
@change="handleRadioChange(children, item)" |
||||
> |
||||
<el-radio border :label="children.pointName">{{ |
||||
children.pointName |
||||
}}</el-radio> |
||||
</el-radio-group> |
||||
</div> |
||||
</template> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import { policyListData } from "@/api/centerairC/strategy"; |
||||
import { operationConrol } from "@/api/hotWater/waterControl"; |
||||
export default { |
||||
name: "deviceStrategy", |
||||
data() { |
||||
return { |
||||
policyList: [], |
||||
policyDetails: [], |
||||
value: "", |
||||
}; |
||||
}, |
||||
mounted() { |
||||
this.getPolicyList(); |
||||
}, |
||||
methods: { |
||||
// 如果有radio,则需要改变policy类名的样式了 |
||||
hasRadioInput(values) { |
||||
return values.some((child) => child.inputType === "radio"); |
||||
}, |
||||
// 请求列表 |
||||
getPolicyList() { |
||||
let data = { |
||||
systemType: "0", |
||||
funPolicyType: "1", |
||||
}; |
||||
policyListData(data).then((res) => { |
||||
console.log("策略返回res", res); |
||||
if (res.code == 200) { |
||||
this.policyList = res.rows; |
||||
this.initRadioSelection(); |
||||
} |
||||
}); |
||||
}, |
||||
// 处理初始页面时候radio的默认选中值 |
||||
initRadioSelection() { |
||||
this.policyList.forEach((item) => { |
||||
item.values.forEach((children) => { |
||||
if (children.inputType === "radio") { |
||||
if (Number(children.curValue) == 0) { |
||||
// console.log("当前项是0", children.curValue); |
||||
children.curValue = null; // 设置为 null 确保 radio 不选中 |
||||
} else { |
||||
console.log("当前项是1", children.curValue); |
||||
const selectedOption = item.values.find( |
||||
(option) => option.curValue === 1 |
||||
); |
||||
// 选中当前的pointName |
||||
if (selectedOption) { |
||||
children.curValue = selectedOption.pointName; |
||||
} |
||||
} |
||||
} |
||||
}); |
||||
}); |
||||
}, |
||||
// 处理输入事件,过滤非数字字符 |
||||
handleInput(children) { |
||||
console.log("校验"); |
||||
// 实时校验并过滤非数字和非小数点字符 |
||||
children.curValue = String(children.curValue).replace(/[^\d.]/g, ""); |
||||
// 确保只有一个小数点 |
||||
const parts = children.curValue.split("."); |
||||
if (parts.length > 2) { |
||||
// 如果有多个小数点,只保留第一个小数点及之前的部分和第一个小数点之后的部分 |
||||
children.curValue = parts[0] + "." + parts[1]; |
||||
} |
||||
// 确保小数点后最多两位 |
||||
if (parts.length === 2) { |
||||
children.curValue = parts[0] + "." + parts[1].slice(0, 2); |
||||
} |
||||
}, |
||||
handleEnter(children) { |
||||
console.log("请求后端", children); |
||||
this.$confirm( |
||||
`确定要修改"${children.pointName}"的数据为:${children.curValue} ${children.unit}吗?`, |
||||
"提示", |
||||
{ |
||||
confirmButtonText: "确定", |
||||
cancelButtonText: "取消", |
||||
type: "warning", |
||||
} |
||||
) |
||||
.then(() => { |
||||
this.hadleOperationConrol(children.cpmId, children.curValue); |
||||
}) |
||||
.catch(() => { |
||||
// 用户取消操作,需要更新原来的频率 |
||||
// this.getOperationList(); |
||||
}); |
||||
}, |
||||
handleRadioChange(children, item) { |
||||
this.$confirm( |
||||
`确定要修改"${item.name}"的数据为:${children.curValue} ${children.unit}吗?`, |
||||
"提示", |
||||
{ |
||||
confirmButtonText: "确定", |
||||
cancelButtonText: "取消", |
||||
type: "warning", |
||||
} |
||||
) |
||||
.then(() => { |
||||
console.log("children.curValue", children.curValue); |
||||
if (children.curValue) { |
||||
this.hadleOperationConrol(children.cpmId, "1"); |
||||
} |
||||
}) |
||||
.catch(() => { |
||||
// 用户取消操作,需要更新原来的数据 |
||||
children.curValue = "0"; |
||||
}); |
||||
}, |
||||
hadleOperationConrol(id, param) { |
||||
let data = { |
||||
id: id, |
||||
param: param, |
||||
}; |
||||
console.log("操作参数", data); |
||||
operationConrol([data]) |
||||
.then((res) => { |
||||
if (res.code == 200) { |
||||
this.$modal.msgSuccess("操作成功"); |
||||
// 更新所有设备状态; |
||||
this.getPolicyList(); |
||||
} else { |
||||
// this.$modal.msgError("操作失败"); |
||||
console.log("应该更新状态的"); |
||||
// 更新所有设备状态; |
||||
this.getPolicyList(); |
||||
} |
||||
}) |
||||
.catch((error) => { |
||||
console.log("请求发生错误,更新设备状态", error); |
||||
// 更新所有设备状态; |
||||
this.getPolicyList(); |
||||
}); |
||||
}, |
||||
}, |
||||
}; |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
.policy-flex { |
||||
margin-top: 15px; |
||||
width: 100%; |
||||
display: flex; |
||||
flex-direction: row; |
||||
flex-wrap: wrap; |
||||
.policy { |
||||
display: flex; |
||||
flex-direction: column; |
||||
position: relative; |
||||
width: calc(33.33% - 40px); |
||||
margin: 0 20px; |
||||
min-height: 300px; |
||||
background-image: url(../../../assets/images/strategy-border.png); |
||||
background-size: 100% 100%; |
||||
border-bottom: 1px solid #1587cc; |
||||
border-left: 1px solid #1587cc; |
||||
margin-bottom: 25px; |
||||
padding: 70px 60px 20px 40px; |
||||
.policy-title { |
||||
font-size: 0.18rem; |
||||
font-weight: bold; |
||||
position: absolute; |
||||
top: 15px; |
||||
left: 25px; |
||||
margin-bottom: 25px; |
||||
} |
||||
.policy-li { |
||||
display: flex; |
||||
flex-direction: row; |
||||
align-items: center; |
||||
justify-content: space-between; |
||||
z-index: 0; |
||||
font-size: 0.16rem; |
||||
color: #94b8ce; |
||||
margin-bottom: 10px; |
||||
.right-data { |
||||
display: flex; |
||||
flex-direction: row; |
||||
align-items: center; |
||||
justify-content: flex-start; |
||||
flex-wrap: wrap; |
||||
// max-width: 2.5rem; |
||||
.unit { |
||||
width: 25px; |
||||
white-space: nowrap; |
||||
} |
||||
} |
||||
.el-input { |
||||
width: 80px !important; |
||||
margin: 0 15px 0 5px; |
||||
} |
||||
} |
||||
} |
||||
.radio-style { |
||||
display: flex; |
||||
flex-direction: row !important; |
||||
align-items: flex-start !important; |
||||
flex-wrap: wrap; |
||||
align-content: flex-start; /* 调整多行元素的对齐方式 */ |
||||
.policy-li { |
||||
margin: 10px; |
||||
width: calc(50% - 20px) !important; |
||||
} |
||||
} |
||||
} |
||||
@media (min-width: 1560px) and (max-width: 1670px) { |
||||
.policy { |
||||
padding: 70px 0.3rem 20px 40px !important; |
||||
} |
||||
} |
||||
@media (min-width: 1400px) and (max-width: 1560px) { |
||||
.policy { |
||||
padding: 70px 0.3rem 20px 40px !important; |
||||
width: calc(33.33% - 20px) !important; |
||||
margin: 0 10px 25px 10px !important; |
||||
} |
||||
.policy-li { |
||||
font-size: 14px !important; |
||||
} |
||||
} |
||||
@media (min-width: 1340px) and (max-width: 1400px) { |
||||
.policy { |
||||
padding: 70px 0.3rem 20px 40px !important; |
||||
width: calc(33.33% - 20px) !important; |
||||
margin: 0 10px 25px 10px !important; |
||||
} |
||||
.policy-li { |
||||
font-size: 14px !important; |
||||
} |
||||
.radio-style { |
||||
padding: 70px 0.3rem 20px 20px !important; |
||||
.policy-li { |
||||
width: calc(50% - 20px) !important; |
||||
} |
||||
} |
||||
} |
||||
@media (min-width: 1025px) and (max-width: 1340px) { |
||||
.policy { |
||||
width: calc(49% - 20px) !important; |
||||
margin: 0 10px 25px 10px !important; |
||||
padding: 70px 30px 15px 40px !important; |
||||
} |
||||
.policy-title { |
||||
font-size: 16px !important; |
||||
} |
||||
.policy-li { |
||||
font-size: 14px !important; |
||||
} |
||||
} |
||||
@media (max-width: 1240px) { |
||||
.policy { |
||||
width: calc(80% - 20px) !important; |
||||
margin: 0 10px 25px 10px !important; |
||||
padding: 70px 60px 15px 40px !important; |
||||
} |
||||
.policy-title { |
||||
font-size: 16px !important; |
||||
left: 40px !important; |
||||
} |
||||
.policy-li { |
||||
font-size: 14px !important; |
||||
} |
||||
} |
||||
</style> |
||||
<style scoped> |
||||
.policy-li >>> .el-input__inner { |
||||
background-color: #04193a; |
||||
border: 1px solid #1262db; |
||||
color: #3ef0fd; |
||||
font-weight: 700; |
||||
text-align: center; |
||||
padding: 0 0.1rem !important; |
||||
} |
||||
.policy-li >>> .el-radio { |
||||
margin-bottom: 5px; |
||||
} |
||||
.policy-li >>> .el-radio__label { |
||||
color: #3ef0fd; |
||||
} |
||||
.policy-li >>> .el-radio__inner { |
||||
border: 1px solid #042063; |
||||
/* background-color: transparent !important; */ |
||||
} |
||||
.policy-li >>> .el-radio.is-bordered { |
||||
border: 1px solid #2c6afa; |
||||
} |
||||
</style> |
@ -0,0 +1,85 @@
|
||||
<template> |
||||
<div class="arrow"> |
||||
<!-- 循环渲染 5 个圆点图片 --> |
||||
<img |
||||
v-for="i in 5" |
||||
:key="i" |
||||
class="yuan" |
||||
src="../../assets/images/yuan.png" |
||||
alt="" |
||||
:class="{ 'animate-yuan': isHovered }" |
||||
/> |
||||
<!-- 渲染右箭头图片 --> |
||||
<img |
||||
class="arrow-l" |
||||
src="../../assets/images/arrow-l.png" |
||||
alt="" |
||||
:class="{ 'animate-arrow': isHovered }" |
||||
/> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
props: { |
||||
isHovered: { |
||||
type: Boolean, |
||||
default: false, |
||||
}, |
||||
}, |
||||
}; |
||||
</script> |
||||
<style lang="scss" scoped> |
||||
.arrow { |
||||
display: flex; |
||||
flex-direction: row; |
||||
align-items: center; |
||||
.yuan { |
||||
width: 6px; |
||||
height: 6px; |
||||
margin-right: 10px; |
||||
} |
||||
.arrow-l { |
||||
width: 15px; |
||||
height: 20px; |
||||
} |
||||
} |
||||
/* 圆点动画 */ |
||||
@keyframes yuanAnimation { |
||||
0% { |
||||
transform: scale(1) translateX(0); |
||||
opacity: 1; |
||||
} |
||||
50% { |
||||
transform: scale(1.2) translateX(5px); /* 增加向右移动 5px,可按需调整 */ |
||||
opacity: 0.6; |
||||
} |
||||
100% { |
||||
transform: scale(1) translateX(0); |
||||
opacity: 1; |
||||
} |
||||
} |
||||
|
||||
/* 箭头动画 */ |
||||
@keyframes arrowAnimation { |
||||
0% { |
||||
transform: translateX(0); |
||||
} |
||||
50% { |
||||
transform: translateX(10px); /* 根据实际情况调整移动距离 */ |
||||
} |
||||
100% { |
||||
transform: translateX(0); |
||||
} |
||||
} |
||||
|
||||
/* 应用圆点动画的类 */ |
||||
.animate-yuan { |
||||
animation: yuanAnimation 1s infinite; |
||||
} |
||||
|
||||
/* 应用箭头动画的类 */ |
||||
.animate-arrow { |
||||
animation: arrowAnimation 1s infinite; |
||||
} |
||||
</style> |
@ -0,0 +1,126 @@
|
||||
<template> |
||||
<div class="monitor-container"> |
||||
<!-- 水箱图片 --> |
||||
<img class="monitor-img3" src="../../assets/images/waterBox.png" /> |
||||
<!-- 表示水的 div --> |
||||
<div class="waves" :style="{ height: waterHeight + '%' }"> |
||||
<div class="wave wave1"></div> |
||||
<div class="wave wave2"></div> |
||||
<div class="wave wave3"></div> |
||||
<div class="wave wave4"></div> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
export default { |
||||
// 接收父组件传来的数字 |
||||
props: { |
||||
waterLevel: { |
||||
type: Number, |
||||
default: 0, |
||||
validator: (value) => { |
||||
// 确保传入的数字在 0 到 100 之间 |
||||
return value >= 0 && value <= 100; |
||||
}, |
||||
}, |
||||
}, |
||||
computed: { |
||||
// 计算水的高度百分比 |
||||
waterHeight() { |
||||
return this.waterLevel; |
||||
}, |
||||
}, |
||||
}; |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
.monitor-container { |
||||
position: relative; |
||||
display: inline-block; |
||||
} |
||||
|
||||
.monitor-img3 { |
||||
width: 1.2rem; |
||||
height: 1.3rem; |
||||
} |
||||
.waves { |
||||
position: absolute; |
||||
bottom: 0; |
||||
left: 0; |
||||
width: 100%; |
||||
/* 修改颜色表示,添加透明度 */ |
||||
background-color: rgba(23, 106, 201, 0.1); |
||||
overflow: hidden; |
||||
.wave { |
||||
position: absolute; |
||||
left: 0; |
||||
top: 0; |
||||
width: 100%; |
||||
height: 100%; |
||||
background: url(../../assets/images/wave.png); |
||||
background-size: 200% 100%; |
||||
} |
||||
.wave1 { |
||||
opacity: 1; |
||||
top: 0; |
||||
z-index: 1000; |
||||
animation: animate 4s linear infinite; |
||||
} |
||||
.wave2 { |
||||
opacity: 0.5; |
||||
top: 0.2rem; |
||||
z-index: 999; |
||||
animation: animate2 4s linear infinite; |
||||
} |
||||
.wave3 { |
||||
opacity: 0.2; |
||||
top: 0.3rem; |
||||
z-index: 999; |
||||
animation: animate 2s linear infinite; |
||||
} |
||||
.wave4 { |
||||
opacity: 0.7; |
||||
top: 0.4rem; |
||||
z-index: 999; |
||||
animation: animate2 2s linear infinite; |
||||
} |
||||
} |
||||
@keyframes animate{ |
||||
0%{ |
||||
background-position-x: 110px; |
||||
} |
||||
100%{ |
||||
background-position-x: 0px; |
||||
} |
||||
} |
||||
@keyframes animate2{ |
||||
0%{ |
||||
background-position-x: 0px; |
||||
} |
||||
100%{ |
||||
background-position-x: 110px; |
||||
} |
||||
} |
||||
|
||||
@media (min-width: 1400px) and (max-width: 1620px) { |
||||
.monitor-img3 { |
||||
width: 110px !important; |
||||
height: 120px !important; |
||||
} |
||||
} |
||||
|
||||
@media (min-width: 1240px) and (max-width: 1400px) { |
||||
.monitor-img3 { |
||||
width: 90px !important; |
||||
height: 100px !important; |
||||
} |
||||
} |
||||
|
||||
@media (max-width: 1240px) { |
||||
.monitor-img3 { |
||||
width: 100px !important; |
||||
height: 110px !important; |
||||
} |
||||
} |
||||
</style> |
@ -1,18 +1,361 @@
|
||||
<template> |
||||
<div>热水监测</div> |
||||
<div class="app-container"> |
||||
<div class="left-tree"> |
||||
<!-- 为 el-tree 设置一个固定的高度和滚动条 --> |
||||
<div style="height: 750px; overflow-y: auto"> |
||||
<el-tree |
||||
ref="tree" |
||||
:data="treeData" |
||||
node-key="id" |
||||
:default-expand-all="false" |
||||
:default-expanded-keys="expandedKeys" |
||||
:auto-expand-parent="true" |
||||
icon-class="none" |
||||
@node-expand="handleNodeExpand" |
||||
@node-collapse="handleNodeCollapse" |
||||
:highlight-current="true" |
||||
@node-click="handleNodeClick" |
||||
> |
||||
<template #default="{ node }"> |
||||
<span class="custom-tree-node"> |
||||
<!-- 根据节点状态动态设置图标类名 --> |
||||
<div class="tree-left"> |
||||
<i :class="getIconClass(node)" class="custom-tree-icon"></i> |
||||
<span class="tree-label">{{ node.label }}</span> |
||||
</div> |
||||
</span> |
||||
</template> |
||||
</el-tree> |
||||
</div> |
||||
</div> |
||||
<div class="right-monitor"> |
||||
<div class="buildingDiv"> |
||||
<img |
||||
class="title-bg" |
||||
src="../../../assets/images/title-bg.png" |
||||
alt="" |
||||
/> |
||||
<div class="title-word">楼层:{{ currentName }}</div> |
||||
</div> |
||||
<div class="details-header"> |
||||
<el-tabs v-model="activeName" @tab-click="handleClick"> |
||||
<el-tab-pane label="热泵" name="first"> </el-tab-pane> |
||||
<el-tab-pane label="高区水泵" name="second"> </el-tab-pane> |
||||
<el-tab-pane label="低区水泵" name="three"> </el-tab-pane> |
||||
<el-tab-pane label="水箱" name="four"> </el-tab-pane> |
||||
</el-tabs> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</template> |
||||
|
||||
<script> |
||||
import { monitorList } from "@/api/centerairC/sysMonitor"; |
||||
export default { |
||||
<script> |
||||
import { spaceTree } from "@/api/region"; |
||||
import { waterOperateList, operationConrol } from "@/api/hotWater/waterControl"; |
||||
export default { |
||||
data() { |
||||
return { |
||||
loading: false, |
||||
treeData: [], |
||||
defaultProps: { |
||||
children: "children", |
||||
label: "label", |
||||
}, |
||||
deviceList: [], |
||||
expandedKeys: [], |
||||
currentId: "", //当前选中高亮的id |
||||
currentName: "", //当前选中的名称 |
||||
currentLevel: "", //当前节点的层级 |
||||
currentParentId: "", //当前节点的上级的id |
||||
|
||||
activeName: "first", //设备管理 |
||||
}; |
||||
}, |
||||
mounted() {}, |
||||
methods: {}, |
||||
mounted() { |
||||
this.getSysBuild(); |
||||
}, |
||||
methods: { |
||||
getSysBuild() { |
||||
spaceTree().then((res) => { |
||||
if (res.code == 200) { |
||||
// 只需要保留热水的系统 |
||||
console.log("楼栋返回值", res); |
||||
let newRes = { ...res }; |
||||
|
||||
if (newRes.data && newRes.data[0] && newRes.data[0].children) { |
||||
newRes.data[0].children = newRes.data[0].children.filter((item) => { |
||||
// 假设子项有一个 label属性,用于检查是否包含 "热水" |
||||
return item.label && item.label.includes("热水"); |
||||
}); |
||||
} |
||||
// 指定要保留的最大层级(从 1 开始计数),这里假设指定为第 4 级 |
||||
const targetLevel = 4; |
||||
// 从 data[0] 开始处理,当前层级为 1 |
||||
if (newRes.data[0]) { |
||||
this.removeChildrenAfterLevel(newRes.data[0], 1, targetLevel); |
||||
} |
||||
console.log("筛选后的新结果", newRes); |
||||
this.treeData = newRes.data; |
||||
this.$nextTick(() => { |
||||
// 默认展开节点 |
||||
this.getExpandedKeys(this.treeData, 2); |
||||
if (this.treeData.length > 0) { |
||||
// 找到最后一层的第一个子节点 |
||||
const lastLevelFirstChild = this.findLastLevelFirstChild( |
||||
this.treeData[0] |
||||
); |
||||
// this.$refs.tree.setCurrentKey( |
||||
// this.treeData[0].children[0].children[0].children[0].id |
||||
// ); |
||||
// 设置当前选中的节点,默认高亮 |
||||
this.$refs.tree.setCurrentKey(lastLevelFirstChild.id); |
||||
// 更新当前节点的信息 |
||||
this.currentId = lastLevelFirstChild.id; |
||||
this.currentLevel = lastLevelFirstChild.level; |
||||
this.currentName = lastLevelFirstChild.label; |
||||
console.log("当前选中节点ID", this.currentId); |
||||
console.log("当前选中节点层级", this.currentLevel); |
||||
console.log("当前选中节点名称", this.currentName); |
||||
} |
||||
}); |
||||
} |
||||
}); |
||||
}, |
||||
// 递归函数,用于去除指定层级往后的 children 数据 |
||||
removeChildrenAfterLevel(obj, currentLevel, targetLevel) { |
||||
if (currentLevel >= targetLevel) { |
||||
// 当达到指定层级时,将 children 属性置为空数组 |
||||
obj.children = []; |
||||
return; |
||||
} |
||||
if (obj.children && obj.children.length > 0) { |
||||
// 若存在 children 数组,则递归处理每个子项 |
||||
for (let i = 0; i < obj.children.length; i++) { |
||||
this.removeChildrenAfterLevel( |
||||
obj.children[i], |
||||
currentLevel + 1, |
||||
targetLevel |
||||
); |
||||
} |
||||
} |
||||
}, |
||||
// 递归函数,找到最后一层的第一个子节点 |
||||
findLastLevelFirstChild(node, level = 1) { |
||||
if (!node.children || node.children.length === 0) { |
||||
return { |
||||
id: node.id, |
||||
level, |
||||
label: node.label, |
||||
}; |
||||
</script> |
||||
} |
||||
return this.findLastLevelFirstChild(node.children[0], level + 1); |
||||
}, |
||||
// 默认只展示一二级菜单 |
||||
getExpandedKeys(nodes, level) { |
||||
nodes.forEach((node) => { |
||||
if (level <= this.currentId + 4) { |
||||
this.expandedKeys.push(node.id); |
||||
} |
||||
if (node.children) { |
||||
this.getExpandedKeys(node.children, level + 1); |
||||
} |
||||
}); |
||||
}, |
||||
// 更换图标 |
||||
getIconClass(node) { |
||||
// console.log("当前图标的节点内容", node); |
||||
if (node.level === 4) { |
||||
// 4级菜单时的图标 |
||||
if (node.expanded) { |
||||
return "el-icon-document-opened"; // 三级菜单展开时的图标类名 |
||||
} |
||||
return "el-icon-document"; // 三级菜单收缩时的图标类名 |
||||
} |
||||
if (node.expanded) { |
||||
return "el-icon-folder-opened"; // 非三级菜单展开时的图标类名 |
||||
} |
||||
return "el-icon-folder-add"; // 非三级菜单收缩时的图标类名 |
||||
}, |
||||
handleNodeExpand(node) { |
||||
// 节点展开时触发 |
||||
}, |
||||
handleNodeCollapse(node) { |
||||
// 节点收缩时触发 |
||||
}, |
||||
// 点击当前节点,保存节点内容 |
||||
handleNodeClick(node, data) { |
||||
console.log("点击的当前节点", node, data); |
||||
if (data.level !== 4) { |
||||
console.log("不是第4层000"); |
||||
// 设置当前选中的节点,默认高亮 |
||||
this.$refs.tree.setCurrentKey(this.currentId); |
||||
console.log("当前选中节点ID", this.currentId); |
||||
console.log("当前选中节点层级", this.currentLevel); |
||||
console.log("当前选中节点名称", this.currentName); |
||||
} else { |
||||
console.log("第4层111"); |
||||
this.currentId = node.id; |
||||
this.currentLevel = data.level; |
||||
this.currentName = node.label; |
||||
// 设置当前选中的节点,默认高亮 |
||||
this.$refs.tree.setCurrentKey(this.currentId); |
||||
console.log("当前选中节点ID", this.currentId); |
||||
console.log("当前选中节点层级", this.currentLevel); |
||||
console.log("当前选中节点名称", this.currentName); |
||||
return; |
||||
} |
||||
}, |
||||
handleClick(tab, event) { |
||||
console.log(tab, event); |
||||
console.log("activeName", this.activeName); |
||||
}, |
||||
}, |
||||
}; |
||||
</script> |
||||
|
||||
<style lang="scss" scoped> |
||||
.app-container { |
||||
display: flex; |
||||
flex-direction: row; |
||||
justify-content: space-between; |
||||
align-items: stretch; |
||||
height: 100%; |
||||
.left-tree { |
||||
width: 216px; |
||||
padding: 15px 10px; |
||||
border: 1px solid #004b8c; |
||||
min-height: 800px; |
||||
.status { |
||||
display: flex; |
||||
flex-direction: row; |
||||
justify-content: space-between; |
||||
flex-wrap: wrap; |
||||
margin-top: 20px; |
||||
.status-li { |
||||
width: 50%; |
||||
display: flex; |
||||
flex-direction: column; |
||||
align-items: center; |
||||
justify-content: center; |
||||
margin-bottom: 15px; |
||||
} |
||||
.status1, |
||||
.status2, |
||||
.status3, |
||||
.status4 { |
||||
display: flex; |
||||
flex-direction: row; |
||||
align-items: center; |
||||
margin-bottom: 5px; |
||||
} |
||||
.status1::before { |
||||
content: ""; |
||||
width: 10px; |
||||
height: 10px; |
||||
border-radius: 50%; |
||||
background-color: #00d2ff; |
||||
margin-right: 5px; |
||||
} |
||||
.status2::before { |
||||
content: ""; |
||||
width: 10px; |
||||
height: 10px; |
||||
border-radius: 50%; |
||||
background-color: #ff2f2f; |
||||
margin-right: 5px; |
||||
} |
||||
.status3::before { |
||||
content: ""; |
||||
width: 10px; |
||||
height: 10px; |
||||
border-radius: 50%; |
||||
background-color: #ff9908; |
||||
margin-right: 5px; |
||||
} |
||||
.status4::before { |
||||
content: ""; |
||||
width: 10px; |
||||
height: 10px; |
||||
border-radius: 50%; |
||||
background-color: #7398c7; |
||||
margin-right: 5px; |
||||
} |
||||
} |
||||
} |
||||
.right-monitor { |
||||
width: calc(100% - 240px); |
||||
display: flex; |
||||
flex-direction: column; |
||||
justify-content: flex-start; |
||||
.buildingDiv { |
||||
padding-left: 54px; |
||||
display: flex; |
||||
flex-direction: row; |
||||
align-items: center; |
||||
margin-bottom: 20px; |
||||
position: relative; |
||||
.title-bg { |
||||
width: 208px; |
||||
height: 38px; |
||||
position: absolute; |
||||
left: 0; |
||||
z-index: 0; |
||||
} |
||||
.title-word { |
||||
z-index: 10; |
||||
font-family: YouSheBiaoTiHei; |
||||
font-size: 24px; |
||||
color: #ffffff; |
||||
white-space: nowrap; |
||||
} |
||||
} |
||||
.monitor-context { |
||||
height: 640px; |
||||
width: 100%; |
||||
overflow-y: auto; |
||||
display: flex; |
||||
flex-direction: row; |
||||
align-items: flex-start; |
||||
justify-content: flex-start; |
||||
flex-wrap: wrap; |
||||
align-content: flex-start; |
||||
} |
||||
} |
||||
} |
||||
.tree-container { |
||||
height: 300px; /* 设置固定高度 */ |
||||
overflow-y: auto; /* 启用垂直滚动条 */ |
||||
} |
||||
// 滚动条 |
||||
:-webkit-scrollbar { |
||||
width: 10px; /* 滚动条宽度 */ |
||||
} |
||||
|
||||
<style></style> |
||||
::-webkit-scrollbar-track { |
||||
background: transparent !important; /* 滚动条轨道背景色 */ |
||||
} |
||||
.custom-tree-node { |
||||
flex: 1; |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: space-between; |
||||
font-size: 14px; |
||||
padding-right: 8px; |
||||
.tree-left { |
||||
.custom-tree-icon { |
||||
margin-right: 5px; |
||||
} |
||||
} |
||||
} |
||||
</style> |
||||
<style scoped> |
||||
/* 自定义高亮颜色 */ |
||||
.left-tree |
||||
>>> .el-tree--highlight-current |
||||
.el-tree-node.is-current |
||||
> .el-tree-node__content { |
||||
background-color: #285b9e !important; |
||||
/* color: #f56c6c; */ |
||||
color: #25f1f8; |
||||
} |
||||
</style> |
||||
|