chenpingsen
4 months ago
14 changed files with 2597 additions and 52 deletions
@ -0,0 +1,28 @@ |
|||||
|
const prefix = '/carbon-smart'; |
||||
|
// 空调系统及相关接口
|
||||
|
export enum airConditionControl { |
||||
|
// 主页 ======================================================
|
||||
|
|
||||
|
// 主页分区结构
|
||||
|
getTree = prefix + '/airConditioningCtrl/getCtrlPanelTree', |
||||
|
// 主页小灯泡
|
||||
|
getDeviceList = prefix + '/airConditioningCtrl/getDeviceState', |
||||
|
|
||||
|
// 主页 > 抽屉 > 控制面板 =======================================
|
||||
|
|
||||
|
// 获得修改的比对列表
|
||||
|
getChangeList = prefix + '/airConditioningCtrl/getSceneChangeInfo', |
||||
|
// 提交修改结果
|
||||
|
submitChangeList = prefix + '/airConditioningCtrl/changeToSceneMode', |
||||
|
|
||||
|
// 主页 > 抽屉 > 计划列表 =======================================
|
||||
|
|
||||
|
// 穿梭框来源数据
|
||||
|
getTransData = prefix + '/airConditioningInfo/getDeActivatedPlanList', |
||||
|
// 右侧表格数据
|
||||
|
getTableData = prefix + '/airConditioningInfo/getActivatedPlanList', |
||||
|
// 穿梭框结果数据提交
|
||||
|
submitTransData = prefix + '/airConditioningInfo/activePlanByIdList', |
||||
|
// 右侧表格修改数据提交
|
||||
|
submitTableData = prefix + '/airConditioningCtrl/refreshPlanStatus', |
||||
|
} |
@ -0,0 +1,157 @@ |
|||||
|
.out-dialog { |
||||
|
position: fixed; |
||||
|
right: 496px; |
||||
|
width: 500px; |
||||
|
height: 100%; |
||||
|
z-index: 0; |
||||
|
top: 0; |
||||
|
bottom: 0; |
||||
|
margin: auto; |
||||
|
box-sizing: border-box; |
||||
|
color: rgb(255, 83, 0); |
||||
|
background: black; |
||||
|
display: flex; |
||||
|
padding: 25px; |
||||
|
flex-direction: column; |
||||
|
transition: all ease 0.5s; |
||||
|
.content { |
||||
|
overflow-y: scroll; |
||||
|
.div-operation { |
||||
|
display: inline-block; |
||||
|
width: 3px; |
||||
|
height: 13px; |
||||
|
opacity: 1; |
||||
|
border-radius: 1px; |
||||
|
background: rgba(67, 136, 251, 1); |
||||
|
} |
||||
|
.text-operation { |
||||
|
display: inline-block; |
||||
|
color: rgba(255, 255, 255, 1); |
||||
|
font-size: 16px; |
||||
|
font-weight: 700; |
||||
|
margin-left: 5px; |
||||
|
} |
||||
|
.j-box { |
||||
|
background-color: #000; |
||||
|
opacity: 1; |
||||
|
z-index: 99999; |
||||
|
overflow-y: scroll; |
||||
|
.journal { |
||||
|
padding: 1% 3%; |
||||
|
width: 100%; |
||||
|
height: 150px; |
||||
|
background-color: rgba(0, 0, 0); |
||||
|
border-radius: 12px; |
||||
|
border: 2px solid transparent; |
||||
|
border-image: linear-gradient(to bottom, #0077ff, #00f6ff, #000000) 1; |
||||
|
.title-img { |
||||
|
padding-top: 6px; |
||||
|
} |
||||
|
} |
||||
|
.imgText { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: space-between; |
||||
|
|
||||
|
.ztzm { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.cxbtn { |
||||
|
color: rgba(255, 255, 255, 1); |
||||
|
border: none; |
||||
|
border-radius: 6px; |
||||
|
width: 59.79px; |
||||
|
height: 32px; |
||||
|
opacity: 1; |
||||
|
background: linear-gradient( |
||||
|
180deg, |
||||
|
rgba(255, 187, 0, 1) 0%, |
||||
|
rgba(255, 112, 3, 1) 91.21%, |
||||
|
rgba(255, 129, 3, 1) 100% |
||||
|
); |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
} |
||||
|
.btn-box { |
||||
|
display: grid; |
||||
|
grid-template-columns: 1fr 1fr; |
||||
|
grid-template-rows: 1fr 1fr; |
||||
|
grid-row-gap: 15px; |
||||
|
.btn-item { |
||||
|
text-align: center; |
||||
|
display: flex; |
||||
|
align-content: space-between; |
||||
|
.left { |
||||
|
width: 70px; |
||||
|
height: 35px; |
||||
|
line-height: 35px; |
||||
|
border-radius: 4px; |
||||
|
background: linear-gradient( |
||||
|
180deg, |
||||
|
rgba(1, 206, 255, 1) 0%, |
||||
|
rgba(0, 150, 229, 1) 100% |
||||
|
); |
||||
|
color: rgba(255, 255, 255, 1); |
||||
|
font-size: 14px; |
||||
|
font-weight: 400; |
||||
|
} |
||||
|
.right { |
||||
|
width: 140px; |
||||
|
height: 35px; |
||||
|
line-height: 35px; |
||||
|
:first-child { |
||||
|
color: white; |
||||
|
} |
||||
|
span { |
||||
|
vertical-align: middle; |
||||
|
} |
||||
|
img { |
||||
|
padding: 0 5px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.button-box { |
||||
|
width: 100%; |
||||
|
box-sizing: border-box; |
||||
|
padding: 10px; |
||||
|
height: 60px; |
||||
|
position: absolute; |
||||
|
background-color: transparent; |
||||
|
bottom: 0; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
.execute, .cancel { |
||||
|
margin-right: 10px; |
||||
|
width: 74px; |
||||
|
height: 40px; |
||||
|
opacity: 1; |
||||
|
cursor: pointer; |
||||
|
border-radius: 4px; |
||||
|
font-size: 14px; |
||||
|
font-weight: 400; |
||||
|
border: 0; |
||||
|
margin-left: 10px; |
||||
|
} |
||||
|
.execute { |
||||
|
background: rgb(67, 136, 251); |
||||
|
color: white; |
||||
|
} |
||||
|
.cancel { |
||||
|
background: white; |
||||
|
color: black; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.fade-enter-active, .fade-leave-active { |
||||
|
transform: translateX(0px); |
||||
|
} |
||||
|
.fade-enter-from, .fade-leave-to { |
||||
|
transform: translateX(496px); |
||||
|
} |
@ -0,0 +1,149 @@ |
|||||
|
// 页面容器 |
||||
|
.lighting-box { |
||||
|
position: relative; |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
// background: linear-gradient(#badaff, #8cabeb, #7095de); |
||||
|
// 照明设备功能总容器 |
||||
|
.lighting-img-box { |
||||
|
position: relative; |
||||
|
width: 1280px; |
||||
|
height: 720px; |
||||
|
user-select: none; |
||||
|
background-image: url(../image/bg.jpg); |
||||
|
// 由于背景是俯视图,会产生有交点的透视效果,故使用透视属性 |
||||
|
perspective: 1000px; |
||||
|
perspective-origin: 850px -160px; |
||||
|
// 左上角区域切换功能 |
||||
|
.btn-box { |
||||
|
width: 120px; |
||||
|
position: sticky; |
||||
|
top: 10px; |
||||
|
left: 10px; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
gap: 8px; |
||||
|
.btn-item { |
||||
|
cursor: pointer; |
||||
|
width: 100%; |
||||
|
height: 40px; |
||||
|
border-radius: 4px; |
||||
|
background: rgba(39, 120, 255, 1); |
||||
|
border: 1px solid rgba(51, 199, 255, 1); |
||||
|
box-shadow: 0px 10px 15px rgba(0, 54, 136, 0.3); |
||||
|
font-size: 18px; |
||||
|
color: white; |
||||
|
} |
||||
|
.btn-item:hover { |
||||
|
color: black; |
||||
|
} |
||||
|
} |
||||
|
// 大区分区 |
||||
|
.area{ |
||||
|
position: absolute; |
||||
|
bottom: 170px; |
||||
|
left: 240px; |
||||
|
width: 780px; |
||||
|
height: 240px; |
||||
|
transform: rotateX(79deg) rotateZ(-22deg) skew(29deg); |
||||
|
display: flex; |
||||
|
gap: 5px; |
||||
|
.area1 { |
||||
|
width: 65px; |
||||
|
background: rgba(0, 251, 91, 0.3); |
||||
|
border: 2px solid rgb(0, 251, 91); |
||||
|
display: flex; |
||||
|
} |
||||
|
.area2 { |
||||
|
width: 100px; |
||||
|
background: rgba(255, 165, 0, 0.3); |
||||
|
border: 2px solid rgb(255, 165, 0); |
||||
|
display: flex; |
||||
|
} |
||||
|
.area3 { |
||||
|
width: 220px; |
||||
|
background: rgba(255, 0, 0, 0.3); |
||||
|
border: 2px solid rgb(255, 0, 0); |
||||
|
display: flex; |
||||
|
} |
||||
|
.area4 { |
||||
|
width: 70px; |
||||
|
background: rgba(80, 236, 244, 0.3); |
||||
|
border: 2px solid rgb(80, 236, 244); |
||||
|
display: flex; |
||||
|
} |
||||
|
.area5 { |
||||
|
flex: 210px; |
||||
|
background: rgba(0, 251, 91, 0.3); |
||||
|
border: 2px solid rgb(0, 251, 91); |
||||
|
display: flex; |
||||
|
} |
||||
|
.area6 { |
||||
|
flex: 1; |
||||
|
background: rgba(255, 165, 0, 0.3); |
||||
|
border: 2px solid rgb(255, 165, 0); |
||||
|
display: flex; |
||||
|
} |
||||
|
.area-item:hover { |
||||
|
transform: scale(1.05); |
||||
|
} |
||||
|
.area-item { |
||||
|
cursor: pointer; |
||||
|
transition: all ease 0.2s; |
||||
|
>.light-group { |
||||
|
height: 100%; |
||||
|
flex: 1; |
||||
|
display:flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
box-sizing: border-box; |
||||
|
padding: 10px; |
||||
|
// border: 2px solid red; |
||||
|
.shadow-box { |
||||
|
width: 30px; |
||||
|
height: 200px; |
||||
|
border-radius: 20px; |
||||
|
background: rgba(0, 0, 0, 0.2); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
// 总容器与抽屉按钮 |
||||
|
.ns-content-main { |
||||
|
position: relative; |
||||
|
// 抽屉打开按钮 |
||||
|
.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; |
||||
|
} |
||||
|
// 抽屉关闭按钮 |
||||
|
.drawer-box-out { |
||||
|
width: 30px; |
||||
|
height: 40px; |
||||
|
border-radius: 2px; |
||||
|
position: fixed; |
||||
|
right: 496px; |
||||
|
top: 0; |
||||
|
bottom: 0; |
||||
|
margin: auto; |
||||
|
background: rgba(0, 0 ,0 ,0.5); |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
|
||||
|
} |
@ -0,0 +1,323 @@ |
|||||
|
<template> |
||||
|
<div class="lighting-box"> |
||||
|
<div class="lighting-img-box"> |
||||
|
<!-- 左上角,区域切换 --> |
||||
|
<div class="btn-box"> |
||||
|
<button |
||||
|
v-for="(item, index) in floorData" |
||||
|
:key="index" |
||||
|
class="btn-item" |
||||
|
:style="{ color: item.dataCode == thisFloor ? 'black' : 'white' }" |
||||
|
@click="changeFloor(item.childList, item.dataCode)" |
||||
|
>{{ item.name }}</button |
||||
|
> |
||||
|
</div> |
||||
|
<!-- 楼层区域 --> |
||||
|
<div class="area"> |
||||
|
<div |
||||
|
v-for="(item, index) in treeData" |
||||
|
:class="computedClass(item.id)" |
||||
|
@click="changeThisArea([item])" |
||||
|
:key="index"> |
||||
|
<div v-for="(v, i) in item.childList" :key="i" class="light-group"> |
||||
|
<div class="group-shadow" @click.stop="changeThisArea([item, v])"> |
||||
|
<div :class="computedClass(v.id)" class="shadow-box"></div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<!-- 照明设备 --> |
||||
|
<div class="lights"> |
||||
|
<light |
||||
|
v-for="(item, index) in bulbs" |
||||
|
:key="index" |
||||
|
:styleObject="item.styleText" |
||||
|
:type="item.type" |
||||
|
:visible="item.visible" /> |
||||
|
</div> |
||||
|
</div> |
||||
|
<!-- 右侧抽屉的触发按钮 --> |
||||
|
<div class="drawer-box-in" v-if="!visible" @click="toggleDrawer"> |
||||
|
<double-left-outlined class="drawer-icon" style="color: white" /> |
||||
|
</div> |
||||
|
<!-- 左侧抽屉的关闭按钮 --> |
||||
|
<div class="drawer-box-out" v-if="visible" @click="toggleDrawer"> |
||||
|
<double-right-outlined class="drawer-icon" style="color: white" /> |
||||
|
</div> |
||||
|
<!-- 右侧抽屉 --> |
||||
|
<a-drawer |
||||
|
v-model:visible="visible" |
||||
|
class="drawer-item" |
||||
|
width="496" |
||||
|
:forceRender="preload" |
||||
|
placement="right" |
||||
|
:body-style="{ background: 'rgba(0, 0, 0)', opacity: 0.8 }" |
||||
|
:closable="false" |
||||
|
id="drawer" |
||||
|
:maskStyle="{ 'background-color': 'rgba(0, 0, 0, 0)' }"> |
||||
|
<a-tabs v-model:activeKey="activeKey"> |
||||
|
<a-tab-pane key="1" tab="控制面板"> |
||||
|
<tabs1 |
||||
|
ref="tabs1Ref" |
||||
|
@changeArea="changeArea" |
||||
|
@reset="reset" |
||||
|
@reload="reload" |
||||
|
:treeData="treeData" /> |
||||
|
</a-tab-pane> |
||||
|
<a-tab-pane key="2" tab="计划列表" force-render> |
||||
|
<tabs2 /> |
||||
|
</a-tab-pane> |
||||
|
<a-tab-pane key="3" tab="日志"> |
||||
|
<tabs3 /> |
||||
|
</a-tab-pane> |
||||
|
</a-tabs> |
||||
|
</a-drawer> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script setup lang="ts"> |
||||
|
import { ref, onMounted } from 'vue'; |
||||
|
// 组件 |
||||
|
import light from './light.vue'; |
||||
|
import tabs1 from './tabs1.vue'; |
||||
|
import tabs2 from './tabs2.vue'; |
||||
|
import tabs3 from './tabs3.vue'; |
||||
|
// 请求 |
||||
|
import { http } from '/nerv-lib/util/http'; |
||||
|
import { airConditionControl } from '/@/api/airConditionControlSystem'; |
||||
|
// ICON |
||||
|
import { DoubleLeftOutlined, DoubleRightOutlined } from '@ant-design/icons-vue'; |
||||
|
// 全局变量 |
||||
|
import { items } from '/@/store/item'; |
||||
|
|
||||
|
// 初始化 ======================================================= |
||||
|
|
||||
|
onMounted(() => { |
||||
|
// 获得分区与线路的结构 |
||||
|
getAllArea(); |
||||
|
// 获得俯视图中的小灯泡 |
||||
|
getBulbs(); |
||||
|
}); |
||||
|
|
||||
|
// 获得全局变量 |
||||
|
const state = items(); |
||||
|
// 预加载flag,获得分区数据后,预加载抽屉,防止获取ref报错 |
||||
|
const preload = ref(false); |
||||
|
|
||||
|
// 分层业务 ===================================================== |
||||
|
|
||||
|
// 所有楼层的数据 |
||||
|
const floorData = ref([]); |
||||
|
// 当前选择的楼层 |
||||
|
const thisFloor = ref('1'); |
||||
|
// 左上角分层切换 |
||||
|
const changeFloor = (area: any, floor: string) => { |
||||
|
thisFloor.value = floor; |
||||
|
// 重置数据 |
||||
|
reset(); |
||||
|
// 重置视图 |
||||
|
changeArea(['1']); |
||||
|
// 切换楼层数据 |
||||
|
treeData.value = area; |
||||
|
// 默认选择第一项 |
||||
|
treeData.value[0].selected = true; |
||||
|
}; |
||||
|
|
||||
|
// 分区业务 ===================================================== |
||||
|
|
||||
|
// 分区结构树 |
||||
|
const treeData = ref([]); |
||||
|
// 当前选中的分区序列 - 用于样式渲染 |
||||
|
const area = ref(['1']); |
||||
|
// 线路内小灯泡 - 此处位置需前端写死 |
||||
|
const bulbs = ref([ |
||||
|
{ |
||||
|
styleText: { left: '180px', bottom: '200px' }, |
||||
|
area: 1, |
||||
|
type: 1, |
||||
|
visible: true, |
||||
|
}, |
||||
|
{ |
||||
|
styleText: { left: '230px', bottom: '125px' }, |
||||
|
area: 1, |
||||
|
type: 2, |
||||
|
visible: true, |
||||
|
}, |
||||
|
{ |
||||
|
styleText: { left: '320px', bottom: '140px' }, |
||||
|
area: 1, |
||||
|
type: 3, |
||||
|
visible: true, |
||||
|
}, |
||||
|
{ |
||||
|
styleText: { left: '245px', bottom: '230px' }, |
||||
|
area: 1, |
||||
|
type: 3, |
||||
|
visible: true, |
||||
|
}, |
||||
|
{ |
||||
|
styleText: { left: '405px', bottom: '230px' }, |
||||
|
area: 2, |
||||
|
type: 3, |
||||
|
visible: true, |
||||
|
}, |
||||
|
{ |
||||
|
styleText: { left: '460px', bottom: '180px' }, |
||||
|
area: 2, |
||||
|
type: 3, |
||||
|
visible: true, |
||||
|
}, |
||||
|
{ |
||||
|
styleText: { left: '360px', bottom: '275px' }, |
||||
|
area: 2, |
||||
|
type: 3, |
||||
|
visible: true, |
||||
|
}, |
||||
|
{ |
||||
|
styleText: { left: '715px', bottom: '320px' }, |
||||
|
area: 3, |
||||
|
type: 1, |
||||
|
visible: true, |
||||
|
}, |
||||
|
]); |
||||
|
// 由子组件控制的分区与线路切换 |
||||
|
const changeArea = (result: any) => { |
||||
|
console.log(result, 'changeArea'); |
||||
|
// 数组 |
||||
|
if (Array.isArray(result)) { |
||||
|
area.value = result; |
||||
|
// 不是数组 |
||||
|
} else { |
||||
|
area.value.length = 0; |
||||
|
area.value[0] = String(result); |
||||
|
} |
||||
|
}; |
||||
|
// 由当前组件控制的分区切换 |
||||
|
const changeThisArea = (result: any) => { |
||||
|
console.log(result, 'changeThisArea'); |
||||
|
// 修改前,将所有选项置空 |
||||
|
reset(); |
||||
|
let level1 = result[0]; |
||||
|
area.value.length = 0; |
||||
|
// 只选择了分区 |
||||
|
if (result.length === 1) { |
||||
|
result[0].selected = true; |
||||
|
area.value.push(result[0].id); |
||||
|
// 控制子组件按钮区 |
||||
|
tabs1Ref.value.changeArea(result[0]); |
||||
|
// 选择了分区 + 线路 |
||||
|
} else if (result.length === 2) { |
||||
|
// 如果没有分区,默认选择第一个 |
||||
|
if (!level1) { |
||||
|
level1 = treeData.value[0]; |
||||
|
} |
||||
|
// 选中状态都设为true |
||||
|
level1.selected = result[1].selected = true; |
||||
|
area.value.splice(0, 0, level1.id, result[1].id); |
||||
|
// 控制子组件按钮区 |
||||
|
tabs1Ref.value.changeArea(result[0]); |
||||
|
tabs1Ref.value.changeLine(result[1]); |
||||
|
} |
||||
|
}; |
||||
|
// 重置分区树所有当前选项 |
||||
|
const reset = () => { |
||||
|
treeData.value.forEach((item: any) => { |
||||
|
item.selected = false; |
||||
|
item.childList.forEach((v: any) => { |
||||
|
v.selected = false; |
||||
|
}); |
||||
|
}); |
||||
|
}; |
||||
|
// 俯视图分区选中计算函数 |
||||
|
const computedClass = (string: string) => { |
||||
|
if (area.value.indexOf(string) != -1) { |
||||
|
return `isActive area-item area${string}`; |
||||
|
} else { |
||||
|
return `area-item area${string}`; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// 获得所有分区 |
||||
|
const getAllArea = (realod = false) => { |
||||
|
http.get(airConditionControl.getTree, { projectId: state.projectId }).then((res) => { |
||||
|
const data = res.data; |
||||
|
floorData.value = data; |
||||
|
/** 只在前端使用的变量 |
||||
|
* @param id 用于判断样式和层级的前端属性 |
||||
|
* @param selected 用于表示是否选中的前端属性 |
||||
|
*/ |
||||
|
// 楼层 1 |
||||
|
data.forEach((floor: any) => { |
||||
|
// 分区 2 |
||||
|
floor.childList.forEach((item: any, index: number) => { |
||||
|
if (index == 0) { |
||||
|
item.selected = true; |
||||
|
} else { |
||||
|
item.selected = false; |
||||
|
} |
||||
|
item.id = String(index + 1); |
||||
|
// 分组 3 |
||||
|
item.childList.forEach((v: any, i: number) => { |
||||
|
v.selected = false; |
||||
|
v.id = index + 1 + '-' + (i + 1); |
||||
|
}); |
||||
|
}); |
||||
|
}); |
||||
|
// 默认展示 线路 1-1,重载则使用thisFloor当前选中的楼层数 |
||||
|
let index = 0; |
||||
|
if (realod) { |
||||
|
data.find((item: any, i: number) => { |
||||
|
if (item.dataCode == thisFloor.value) { |
||||
|
return (index = i); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
treeData.value = data[index].childList; |
||||
|
// 开始预加载 |
||||
|
preload.value = true; |
||||
|
}); |
||||
|
}; |
||||
|
const reload = () => { |
||||
|
getAllArea(true); |
||||
|
}; |
||||
|
|
||||
|
// 设备业务 小灯泡 ============================================== |
||||
|
|
||||
|
const getBulbs = () => { |
||||
|
http |
||||
|
.get(airConditionControl.getDeviceList, { |
||||
|
floor: thisFloor.value, |
||||
|
projectId: state.projectId, |
||||
|
siteId: state.siteId, |
||||
|
}) |
||||
|
.then((res) => { |
||||
|
console.log(res); |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
// 抽屉业务 ===================================================== |
||||
|
|
||||
|
// 抽屉 - 当前选择的tab |
||||
|
let activeKey = ref('1'); |
||||
|
// 抽屉 - 打开状态 |
||||
|
let visible = ref(false); |
||||
|
// 抽屉 - 开关事件 |
||||
|
const toggleDrawer = () => { |
||||
|
visible.value = !visible.value; |
||||
|
}; |
||||
|
// 抽屉tab1组件的引用 |
||||
|
const tabs1Ref = ref(); |
||||
|
</script> |
||||
|
<style lang="less" scoped> |
||||
|
@import './index.less'; |
||||
|
|
||||
|
// 当前选中分区的样式 |
||||
|
.isActive { |
||||
|
border: 3px solid white !important; |
||||
|
} |
||||
|
|
||||
|
// 抽屉顶部tab按钮样式 |
||||
|
:deep(.ant-tabs-tab-btn) { |
||||
|
color: white; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,149 @@ |
|||||
|
<template> |
||||
|
<a-popover color="rgba(0, 0, 0, 0.8)"> |
||||
|
<template #content> |
||||
|
<div class="light-tag"> |
||||
|
<div class="light-tag-tit"> |
||||
|
<div> |
||||
|
<img src="/asset/image//bulbLogo/21962.png" alt="" /> |
||||
|
<span class="tag-text">{{ result.name }}</span> |
||||
|
</div> |
||||
|
<button class="right-button">{{ type == 1 ? '正常' : '关闭' }}</button> |
||||
|
</div> |
||||
|
<div class="light-tag-box"> |
||||
|
<div class="tag-box-item"> |
||||
|
<img src="/asset/image//bulbLogo/22496.png" alt="" /> |
||||
|
<span class="title">控制模式</span> |
||||
|
<span class="result">自动</span> |
||||
|
</div> |
||||
|
<div class="tag-box-item"> |
||||
|
<img src="/asset/image//bulbLogo/22496.png" alt="" /> |
||||
|
<span class="title">亮度</span> |
||||
|
<span class="result">{{ result.brightness }}</span> |
||||
|
</div> |
||||
|
<div class="tag-box-item"> |
||||
|
<img src="/asset/image//bulbLogo/22496.png" alt="" /> |
||||
|
<span class="title">控制场景</span> |
||||
|
<span class="result">{{ result.automatic2 }}</span> |
||||
|
</div> |
||||
|
<div class="tag-box-item"> |
||||
|
<img src="/asset/image//bulbLogo/22496.png" alt="" /> |
||||
|
<span class="title">色温</span> |
||||
|
<span class="result">{{ result.brightness3 }}</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
<div class="icon-box" :style="styleObject" v-show="visible"> |
||||
|
<img v-if="type == 1" class="icon-item" src="/asset/image/bulbLogo/22394.png" alt="" /> |
||||
|
<img v-if="type == 2" class="icon-item" src="/asset/image/bulbLogo/22396.png" alt="" /> |
||||
|
<img v-if="type == 3" class="icon-item" src="/asset/image/bulbLogo/22400.png" alt="" /> |
||||
|
</div> |
||||
|
</a-popover> |
||||
|
</template> |
||||
|
|
||||
|
<script setup lang="ts"> |
||||
|
import { ref } from 'vue'; |
||||
|
|
||||
|
defineProps({ |
||||
|
styleObject: Object, |
||||
|
type: Number, |
||||
|
visible: { |
||||
|
type: Boolean, |
||||
|
default: false, |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
const result = ref({ |
||||
|
id: '1', |
||||
|
name: '站厅照明 1区', |
||||
|
manual: '手动', |
||||
|
automatic: '自动', |
||||
|
brightness: '100lux', |
||||
|
brightness2: '30lux', |
||||
|
manual2: '手动', |
||||
|
automatic2: '舒适', |
||||
|
brightness3: '4000k', |
||||
|
brightness4: '3800k', |
||||
|
}); |
||||
|
</script> |
||||
|
<style lang="less" scoped> |
||||
|
.icon-box { |
||||
|
width: 25px; |
||||
|
height: 25px; |
||||
|
position: absolute; |
||||
|
|
||||
|
.icon-item { |
||||
|
width: 25px; |
||||
|
cursor: pointer; |
||||
|
transition: all ease 0.1s; |
||||
|
} |
||||
|
|
||||
|
.icon-item:active { |
||||
|
transform: scale(1.2); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 悬浮窗外部 |
||||
|
.light-tag { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
gap: 10px; |
||||
|
|
||||
|
// 悬浮窗标题 |
||||
|
.light-tag-tit { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
vertical-align: middle; |
||||
|
height: 30px; |
||||
|
|
||||
|
.tag-text { |
||||
|
font-size: 16px; |
||||
|
font-weight: 700; |
||||
|
color: white; |
||||
|
padding-left: 10px; |
||||
|
} |
||||
|
|
||||
|
.right-button { |
||||
|
width: 50px; |
||||
|
height: 26px; |
||||
|
background: rgba(57, 215, 187, 0.1); |
||||
|
border: 1px solid rgba(57, 215, 187, 1); |
||||
|
font-size: 12px; |
||||
|
color: rgba(57, 215, 187, 1); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 悬浮窗底部4格子 |
||||
|
.light-tag-box { |
||||
|
display: grid; |
||||
|
grid-template-columns: 1fr 1fr; |
||||
|
grid-template-rows: 1fr 1fr; |
||||
|
grid-gap: 10px; |
||||
|
|
||||
|
.tag-box-item { |
||||
|
position: relative; |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
|
||||
|
img { |
||||
|
width: 120px; |
||||
|
vertical-align: middle; |
||||
|
} |
||||
|
|
||||
|
.title { |
||||
|
position: absolute; |
||||
|
left: 60px; |
||||
|
top: 13px; |
||||
|
color: rgba(64, 255, 252, 1); |
||||
|
} |
||||
|
|
||||
|
.result { |
||||
|
line-height: 50px; |
||||
|
font-size: 16px; |
||||
|
font-weight: 700; |
||||
|
color: white; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,3 @@ |
|||||
|
// 此文件只定义light(即俯视图小灯泡)的位置信息
|
||||
|
// 与显示状态和分组信息无关
|
||||
|
export const lightPosition = []; |
@ -0,0 +1,403 @@ |
|||||
|
// 抽屉 |
||||
|
.drawer-item { |
||||
|
|
||||
|
.light-area, |
||||
|
.circuit-area, |
||||
|
.control-area, |
||||
|
.control-scene-area, |
||||
|
.light-parameters-area { |
||||
|
width: 100%; |
||||
|
margin-top: 20px; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.light-area-tab, |
||||
|
.circuit-tab, |
||||
|
.control-tab, |
||||
|
.control-scene-tab, |
||||
|
.light-parameters-tab { |
||||
|
width: 5px; |
||||
|
height: 23px; |
||||
|
opacity: 1; |
||||
|
background: rgba(26, 174, 251, 1); |
||||
|
margin-right: 4px; |
||||
|
} |
||||
|
|
||||
|
.light-area-text, |
||||
|
.circuit-text, |
||||
|
.control-text, |
||||
|
.control-scene-text, |
||||
|
.light-parameters-text { |
||||
|
font-size: 14px; |
||||
|
color: white; |
||||
|
padding-left: 5px; |
||||
|
line-height: 23px; |
||||
|
width: 110px; |
||||
|
height: 23px; |
||||
|
background: linear-gradient(270deg, rgba(86, 221, 253, 0) 0%, rgba(25, 176, 255, 1) 100%); |
||||
|
user-select: none; |
||||
|
} |
||||
|
|
||||
|
.light-area-tab, |
||||
|
.light-area-text { |
||||
|
display: inline-block; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
.openPlan { |
||||
|
cursor: pointer; |
||||
|
border: none; |
||||
|
font-size: 14px; |
||||
|
font-weight: 400; |
||||
|
border-radius: 5px; |
||||
|
color: white; |
||||
|
vertical-align: top; |
||||
|
width: 88px; |
||||
|
height: 32px; |
||||
|
} |
||||
|
.openPlan.enabled2 { |
||||
|
background: linear-gradient(180deg, rgba(103, 222, 0, 1) 0%, rgba(0, 181, 6, 1) 100%); |
||||
|
} |
||||
|
|
||||
|
.openPlan.disabled2 { |
||||
|
background-color: red; |
||||
|
} |
||||
|
|
||||
|
.openPlan:disabled { |
||||
|
cursor: not-allowed; |
||||
|
} |
||||
|
|
||||
|
.btn2 { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
margin-left: 80px; |
||||
|
} |
||||
|
|
||||
|
.openzm { |
||||
|
cursor: pointer; |
||||
|
color: rgba(34, 183, 255, 1); |
||||
|
margin-left: 20px; |
||||
|
font-size: 14px; |
||||
|
} |
||||
|
|
||||
|
.custom-checkbox { |
||||
|
width: 13px; |
||||
|
height: 13px; |
||||
|
} |
||||
|
|
||||
|
.both { |
||||
|
width: 59.79px; |
||||
|
height: 32px; |
||||
|
opacity: 1; |
||||
|
background: linear-gradient(180deg, |
||||
|
rgba(255, 187, 0, 1) 0%, |
||||
|
rgba(255, 112, 3, 1) 91.21%, |
||||
|
rgba(255, 129, 3, 1) 100%); |
||||
|
margin-left: 8px; |
||||
|
font-size: 12px; |
||||
|
color: white; |
||||
|
border: none; |
||||
|
border-radius: 5px; |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
|
||||
|
.btn2 { |
||||
|
button { |
||||
|
margin: 0 5px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.allBtn { |
||||
|
border: 0; |
||||
|
width: 40px; |
||||
|
color: white; |
||||
|
background-color: transparent; |
||||
|
} |
||||
|
|
||||
|
.blue-background.ant-switch-checked { |
||||
|
background-color: linear-gradient(180deg, |
||||
|
rgba(1, 206, 255, 1) 0%, |
||||
|
rgba(0, 150, 229, 1) 100%) !important; |
||||
|
} |
||||
|
|
||||
|
.grey-background.ant-switch { |
||||
|
background-color: grey !important; |
||||
|
} |
||||
|
|
||||
|
.blue-background.ant-switch-checked .ant-switch-handle { |
||||
|
background-color: linear-gradient(180deg, |
||||
|
rgba(1, 206, 255, 1) 0%, |
||||
|
rgba(0, 150, 229, 1) 100%) !important; |
||||
|
} |
||||
|
|
||||
|
.grey-background.ant-switch .ant-switch-handle { |
||||
|
background-color: grey !important; |
||||
|
} |
||||
|
|
||||
|
p { |
||||
|
color: white; |
||||
|
} |
||||
|
|
||||
|
.area, |
||||
|
.btnArea, |
||||
|
.control-mode-btn-area, |
||||
|
.control-scene-btn-area { |
||||
|
margin-left: -17px; |
||||
|
|
||||
|
button { |
||||
|
width: 21%; |
||||
|
padding: 0 2%; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.btn { |
||||
|
width: 92px; |
||||
|
height: 40px; |
||||
|
border-radius: 4px; |
||||
|
opacity: 1; |
||||
|
margin-top: 10px; |
||||
|
margin-left: 17px; |
||||
|
font-size: 14px; |
||||
|
font-weight: 400; |
||||
|
opacity: 1; |
||||
|
border: 1px solid rgba(207, 212, 219, 1); |
||||
|
line-height: 20.27px; |
||||
|
color: white; |
||||
|
text-align: center; |
||||
|
vertical-align: top; |
||||
|
background-color: rgba(255, 255, 255, 0.1); |
||||
|
} |
||||
|
|
||||
|
.selected { |
||||
|
background: linear-gradient(180deg, rgba(201, 245, 255, 1) 0%, rgba(138, 215, 255, 1) 100%); |
||||
|
color: rgba(0, 61, 90, 1); |
||||
|
border: 1px solid white; |
||||
|
} |
||||
|
|
||||
|
.btn:hover { |
||||
|
background-color: rgba(207, 212, 219, 1); |
||||
|
} |
||||
|
|
||||
|
.btn:active { |
||||
|
background-color: rgba(102, 102, 102, 1); |
||||
|
color: white; |
||||
|
} |
||||
|
|
||||
|
.circuit-area, |
||||
|
.control-scene-area, |
||||
|
.light-parameters-area { |
||||
|
left: 51px; |
||||
|
width: 100%; |
||||
|
margin-top: 20px; |
||||
|
} |
||||
|
|
||||
|
.circuit-tab, |
||||
|
.circuit-text { |
||||
|
display: inline-block; |
||||
|
} |
||||
|
|
||||
|
.batch { |
||||
|
width: 60px; |
||||
|
height: 32px; |
||||
|
opacity: 1; |
||||
|
border: 1px solid rgba(67, 136, 251, 1); |
||||
|
color: rgba(67, 136, 251, 1); |
||||
|
border-radius: 5px; |
||||
|
background-color: white; |
||||
|
margin-left: 11px; |
||||
|
} |
||||
|
|
||||
|
.control-area { |
||||
|
left: 51px; |
||||
|
width: 100%; |
||||
|
margin-top: 20px; |
||||
|
} |
||||
|
|
||||
|
.control-tab, |
||||
|
.control-text { |
||||
|
display: inline-block; |
||||
|
} |
||||
|
|
||||
|
:deep(.cell) { |
||||
|
text-align: center; |
||||
|
} |
||||
|
|
||||
|
:deep(#pane-first) { |
||||
|
height: 100%; |
||||
|
} |
||||
|
|
||||
|
.control-scene-tab, |
||||
|
.control-scene-text { |
||||
|
display: inline-block; |
||||
|
} |
||||
|
|
||||
|
.light-parameters-tab, |
||||
|
.light-parameters-text { |
||||
|
display: inline-block; |
||||
|
} |
||||
|
|
||||
|
.light-parameters-textarea>p { |
||||
|
height: 100%; |
||||
|
display: flex; |
||||
|
border: 1px solid rgba(236, 239, 245, 1); |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.bottom { |
||||
|
width: 100%; |
||||
|
height: 64px; |
||||
|
display: flex; |
||||
|
justify-content: flex-end; |
||||
|
align-items: center; |
||||
|
position: fixed; |
||||
|
bottom: 0; |
||||
|
right: 0; |
||||
|
} |
||||
|
|
||||
|
.execute { |
||||
|
margin-right: 20px; |
||||
|
width: 74px; |
||||
|
height: 40px; |
||||
|
opacity: 1; |
||||
|
cursor: pointer; |
||||
|
border-radius: 4px; |
||||
|
background: rgba(67, 136, 251, 1); |
||||
|
font-size: 14px; |
||||
|
font-weight: 400; |
||||
|
color: white; |
||||
|
border: 0; |
||||
|
margin: 0 10px; |
||||
|
} |
||||
|
|
||||
|
.flushed { |
||||
|
width: 74px; |
||||
|
height: 40px; |
||||
|
cursor: pointer; |
||||
|
opacity: 1; |
||||
|
border-radius: 4px; |
||||
|
font-size: 14px; |
||||
|
font-weight: 400; |
||||
|
color: rgba(102, 102, 102, 1); |
||||
|
background: white; |
||||
|
border: 1px solid rgba(193, 197, 204, 1); |
||||
|
margin: 0 10px; |
||||
|
} |
||||
|
|
||||
|
:deep(.ant-table-pagination) { |
||||
|
visibility: hidden; |
||||
|
} |
||||
|
|
||||
|
.drawer-content { |
||||
|
margin-left: 20px; |
||||
|
} |
||||
|
|
||||
|
.arrow-indicator { |
||||
|
position: absolute; |
||||
|
top: 50%; |
||||
|
left: 0; |
||||
|
transform: translateY(-50%); |
||||
|
z-index: 1; |
||||
|
} |
||||
|
|
||||
|
.drawer-title1 { |
||||
|
position: fixed; |
||||
|
width: 33px; |
||||
|
height: 33px; |
||||
|
top: 0; |
||||
|
bottom: 0; |
||||
|
right: 20px; |
||||
|
margin: auto; |
||||
|
z-index: 99999; |
||||
|
} |
||||
|
|
||||
|
.drawer-title2 { |
||||
|
position: fixed; |
||||
|
width: 33px; |
||||
|
height: 33px; |
||||
|
top: 0; |
||||
|
bottom: 0; |
||||
|
right: 495px; |
||||
|
margin: auto; |
||||
|
z-index: 99999; |
||||
|
} |
||||
|
|
||||
|
.arrowbtn { |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
width: 28px; |
||||
|
height: 28px; |
||||
|
background: rgba(0, 0, 0, 1); |
||||
|
opacity: 0.5; |
||||
|
border: none; |
||||
|
} |
||||
|
|
||||
|
:deep(.ant-tabs-tab-btn) { |
||||
|
color: white; |
||||
|
} |
||||
|
|
||||
|
:deep(.ant-table) { |
||||
|
background-color: transparent; |
||||
|
} |
||||
|
|
||||
|
:deep(.ant-table-bordered) { |
||||
|
background-color: transparent; |
||||
|
} |
||||
|
|
||||
|
:deep(.ant-table-thead) { |
||||
|
background-color: transparent; |
||||
|
} |
||||
|
|
||||
|
:deep(.ant-table-cell) { |
||||
|
background-color: transparent; |
||||
|
color: #fff; |
||||
|
} |
||||
|
|
||||
|
.custom-table { |
||||
|
border-collapse: collapse; |
||||
|
width: 416px; |
||||
|
height: 60px; |
||||
|
color: rgba(255, 255, 255, 1); |
||||
|
} |
||||
|
|
||||
|
.custom-table th, |
||||
|
.custom-table td { |
||||
|
border: 1px solid rgba(163, 192, 243, 1); |
||||
|
text-align: left; |
||||
|
padding: 8px; |
||||
|
text-align: center; |
||||
|
} |
||||
|
|
||||
|
.table1 { |
||||
|
margin-top: 20px; |
||||
|
width: 100%; |
||||
|
cellspacing: 0; |
||||
|
cellpadding: 0; |
||||
|
border: 1px solid rgba(255, 255, 255); |
||||
|
border-radius: 5px; |
||||
|
background: rgba(255, 255, 255, 0.1); |
||||
|
} |
||||
|
|
||||
|
.light-area, |
||||
|
.circuit-area, |
||||
|
.control-area, |
||||
|
.control-scene-area, |
||||
|
.light-parameters-area { |
||||
|
width: 100%; |
||||
|
margin-top: 20px; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.zmhlbtn { |
||||
|
position: relative; |
||||
|
} |
||||
|
|
||||
|
// 禁用图标 |
||||
|
.anticon-stop { |
||||
|
position: absolute; |
||||
|
right: 3px; |
||||
|
bottom: 3px; |
||||
|
} |
||||
|
} |
@ -0,0 +1,754 @@ |
|||||
|
<template> |
||||
|
<a-spin :spinning="isLoading"> |
||||
|
<!-- 照明区域 --> |
||||
|
<div> |
||||
|
<div class="light-area"> |
||||
|
<div class="light-area-tab"></div> |
||||
|
<span class="light-area-text">空调区域</span> |
||||
|
</div> |
||||
|
<!-- 照明区域按钮部分 --> |
||||
|
<div class="area"> |
||||
|
<template v-if="!showAllButtonsArea"> |
||||
|
<button |
||||
|
v-for="(button, index) in limitedButtons1" |
||||
|
:key="index" |
||||
|
:class="{ btn: true, selected: button.selected }" |
||||
|
@click="changeArea(button)"> |
||||
|
{{ button.name }} |
||||
|
</button> |
||||
|
<div style="margin-top: 10px"> |
||||
|
<span @click="showAllButtonsArea = true" class="openzm"><down-outlined /> 展开</span> |
||||
|
</div> |
||||
|
</template> |
||||
|
<template v-else> |
||||
|
<button |
||||
|
v-for="(button, index) in props.treeData" |
||||
|
:key="index" |
||||
|
:class="{ btn: true, selected: button.selected }" |
||||
|
@click="changeArea(button)"> |
||||
|
{{ button.name }} |
||||
|
</button> |
||||
|
<div style="margin-top: 10px"> |
||||
|
<span @click="showAllButtonsArea = false" class="openzm"><up-outlined /> 回缩</span> |
||||
|
</div> |
||||
|
</template> |
||||
|
</div> |
||||
|
</div> |
||||
|
<!-- 照明回路部分 --> |
||||
|
<div> |
||||
|
<div class="circuit-area"> |
||||
|
<div class="circuit-tab"></div> |
||||
|
<span class="circuit-text">空调分组</span> |
||||
|
<div class="btn2"> |
||||
|
<button |
||||
|
class="openPlan" |
||||
|
:class="{ enabled2: isPlanEnabled2, disabled2: !isPlanEnabled2 }" |
||||
|
@click="togglePlan2"> |
||||
|
{{ isPlanEnabled2 ? '启用开关' : '禁用开关' }} |
||||
|
</button> |
||||
|
<a-switch |
||||
|
v-model:checked="selectAllCheckbox" |
||||
|
:disabled="singleSelection" |
||||
|
:class="{ |
||||
|
'blue-background': selectAllCheckbox, |
||||
|
'grey-background': !selectAllCheckbox, |
||||
|
}" |
||||
|
@change="toggleAllSelection" /> |
||||
|
<button class="allBtn">全选</button> |
||||
|
<button class="both" @click="selectAll"> |
||||
|
{{ singleSelection ? '多选' : '单选' }} |
||||
|
</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="btnArea"> |
||||
|
<template v-if="!showAllButtons"> |
||||
|
<button |
||||
|
v-for="(button, index) in limitedButtons2" |
||||
|
:key="index" |
||||
|
:class="{ btn: true, selected: button.selected }" |
||||
|
class="zmhlbtn" |
||||
|
@click="changeLine(button)"> |
||||
|
<stop-outlined v-if="button.ctrlStatus" /> |
||||
|
{{ button.name }} |
||||
|
</button> |
||||
|
<div style="margin-top: 10px"> |
||||
|
<span @click="showAllButtons = true" class="openzm"><down-outlined /> 展开</span> |
||||
|
</div> |
||||
|
</template> |
||||
|
<template v-else> |
||||
|
<button |
||||
|
v-for="(button, index) in buttons2" |
||||
|
:key="index" |
||||
|
:class="{ btn: true, selected: button.selected }" |
||||
|
class="zmhlbtn" |
||||
|
@click="changeLine(button)"> |
||||
|
<stop-outlined v-if="button.ctrlStatus" /> |
||||
|
{{ button.name }} |
||||
|
</button> |
||||
|
<div style="margin-top: 10px"> |
||||
|
<span @click="showAllButtons = false" class="openzm"><up-outlined /> 回缩</span> |
||||
|
</div> |
||||
|
</template> |
||||
|
</div> |
||||
|
</div> |
||||
|
<!-- 控制模式部分 --> |
||||
|
<div v-show="thisButton2.dataCode"> |
||||
|
<div class="control-area"> |
||||
|
<div class="control-tab"></div> |
||||
|
<span class="control-text">控制模式</span> |
||||
|
</div> |
||||
|
<!-- 控制模式按钮部分 --> |
||||
|
<div class="control-mode-btn-area"> |
||||
|
<button |
||||
|
v-for="(button3, index) in thisButton2.childList" |
||||
|
:key="index" |
||||
|
class="btn" |
||||
|
:class="{ selected: button3.selectAble }" |
||||
|
@click="selectButton3(button3)"> |
||||
|
{{ button3.name }} |
||||
|
</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
<!-- 控制场景部分 --> |
||||
|
<div v-show="thisButton2.dataCode"> |
||||
|
<div class="control-scene-area"> |
||||
|
<div class="control-scene-tab"></div> |
||||
|
<span class="control-scene-text">启动模式</span> |
||||
|
<div v-if="!singleSelection" style="flex: 1; color: red; text-align: right" |
||||
|
>多选模式下会修改当前选中的所有回路</div |
||||
|
> |
||||
|
</div> |
||||
|
<!-- 控制场景按钮部分 --> |
||||
|
<div class="control-scene-btn-area"> |
||||
|
<button |
||||
|
v-for="(button4, index) in thisButton3.childList" |
||||
|
:key="index" |
||||
|
:class="{ btn: true, selected: button4.executeStatus.value != 0 }" |
||||
|
@click="selectButton4(button4)"> |
||||
|
{{ button4.name }} |
||||
|
</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
<!-- 底部按钮区 --> |
||||
|
<div class="bottom"> |
||||
|
<a-badge :offset="[-10, 2]" :count="changeList.length"> |
||||
|
<a-popconfirm |
||||
|
title="刷新将会取消已作出的修改" |
||||
|
ok-text="确定" |
||||
|
cancel-text="取消" |
||||
|
@confirm="refresh" |
||||
|
@cancel="changeCancel"> |
||||
|
<button class="flushed">刷新</button> |
||||
|
</a-popconfirm> |
||||
|
</a-badge> |
||||
|
|
||||
|
<a-spin :spinning="buttonLoading"> |
||||
|
<button class="execute" @click="showModal">执行</button> |
||||
|
</a-spin> |
||||
|
</div> |
||||
|
|
||||
|
<!-- 点击执行时的弹出框 --> |
||||
|
<div class="out-dialog" v-if="executeVisible"> |
||||
|
<div class="content"> |
||||
|
<div> |
||||
|
<div class="div-operation"></div> |
||||
|
<span class="text-operation">变更内容 </span> |
||||
|
</div> |
||||
|
<div class="j-box" v-for="item in diffList" :key="item.id"> |
||||
|
<div class="journal" style="margin-top: 20px"> |
||||
|
<div class="imgText"> |
||||
|
<div class="zjzm"> |
||||
|
<img class="title-img" src="/asset/image//bulbLogo/21961.png" alt="" /> |
||||
|
<span |
||||
|
class="title-text" |
||||
|
style="font-size: 20px; font-weight: 500; color: rgba(255, 255, 255, 1)" |
||||
|
>{{ item.regionName + ' > ' + item.deviceGroupName }}</span |
||||
|
> |
||||
|
</div> |
||||
|
<a-popconfirm |
||||
|
title="此操作将会撤销修改" |
||||
|
ok-text="确定" |
||||
|
cancel-text="取消" |
||||
|
@confirm="delBtn(item)" |
||||
|
@cancel="changeCancel"> |
||||
|
<button class="cxbtn">撤销</button> |
||||
|
</a-popconfirm> |
||||
|
</div> |
||||
|
<div class="btn-box"> |
||||
|
<div class="btn-item"> |
||||
|
<div class="left">控制模式</div> |
||||
|
<div class="right"> |
||||
|
<span>手动</span> |
||||
|
<img src="/asset/image/bulbLogo/22406.png" alt="" /> |
||||
|
<span>自动</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="btn-item"> |
||||
|
<div class="left"> 亮度 </div> |
||||
|
<div class="right"> |
||||
|
<!-- 由于数字0也会被判为false,故只判断undefined 和 null --> |
||||
|
<span>{{ |
||||
|
item?.stateBefore?.brightness != undefined ? item.stateBefore.brightness : '--' |
||||
|
}}</span> |
||||
|
<img src="/asset/image/bulbLogo/22406.png" alt="" /> |
||||
|
<span>{{ |
||||
|
item?.stateAfter?.brightness != undefined ? item.stateAfter.brightness : '--' |
||||
|
}}</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="btn-item"> |
||||
|
<div class="left"> 控制场景 </div> |
||||
|
<div class="right"> |
||||
|
<span>{{ |
||||
|
item?.stateBefore?.scene?.label ? item.stateBefore.scene.label : '--' |
||||
|
}}</span> |
||||
|
<img src="/asset/image/bulbLogo/22406.png" alt="" /> |
||||
|
<span>{{ |
||||
|
item?.stateAfter?.scene?.label ? item.stateAfter.scene.label : '--' |
||||
|
}}</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="btn-item"> |
||||
|
<div class="left"> 色温 </div> |
||||
|
<div class="right"> |
||||
|
<span>{{ |
||||
|
item?.stateBefore?.color != undefined ? item.stateBefore.color : '--' |
||||
|
}}</span> |
||||
|
<img src="/asset/image/bulbLogo/22406.png" alt="" /> |
||||
|
<span> |
||||
|
{{ item?.stateAfter?.color != undefined ? item.stateAfter.color : '--' }} |
||||
|
</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div style="width: 100%; height: 60px"></div> |
||||
|
<div class="button-box"> |
||||
|
<button class="cancel" @click="executeVisible = false">取消</button> |
||||
|
<a-popconfirm |
||||
|
title="此操作将提交以上修改内容" |
||||
|
ok-text="确定" |
||||
|
cancel-text="取消" |
||||
|
@click="submitChangeList" |
||||
|
@cancel="changeCancel"> |
||||
|
<button class="execute">执行</button> |
||||
|
</a-popconfirm> |
||||
|
</div> |
||||
|
</div> |
||||
|
</a-spin> |
||||
|
</template> |
||||
|
|
||||
|
<script setup lang="ts"> |
||||
|
import { ref, computed, onMounted } from 'vue'; |
||||
|
import { DownOutlined, UpOutlined, StopOutlined } from '@ant-design/icons-vue'; |
||||
|
import { message } from 'ant-design-vue'; |
||||
|
// 请求 |
||||
|
import { http } from '/nerv-lib/util/http'; |
||||
|
import { lightingManage } from '/@/api/IlluminationInfo'; |
||||
|
import { airConditionControl } from '/@/api/airConditionControlSystem'; |
||||
|
// 全局变量 |
||||
|
import { items } from '/@/store/item'; |
||||
|
|
||||
|
// 初始化 ========================================================================= |
||||
|
|
||||
|
onMounted(() => { |
||||
|
// 分区初始化 |
||||
|
setArea(); |
||||
|
}); |
||||
|
// 分区初始化,以 1-1 作为默认回路 |
||||
|
const setArea = () => { |
||||
|
const data = props.treeData[0]; |
||||
|
buttons2.value = data.childList; |
||||
|
}; |
||||
|
// 全局变量 |
||||
|
const state = items(); |
||||
|
|
||||
|
// 与父组件的交互 =================================================================== |
||||
|
|
||||
|
const props = defineProps({ |
||||
|
// 分区结构(照明区域 > 照明回路) |
||||
|
treeData: { |
||||
|
type: Array, |
||||
|
default: () => { |
||||
|
[]; |
||||
|
}, |
||||
|
}, |
||||
|
}); |
||||
|
/** 向上传递方法 |
||||
|
* @method changeArea 用于控制俯视图的选中状态 |
||||
|
* @method reset 用于重置按钮区 |
||||
|
* @method reload 用于刷新一次页面 |
||||
|
*/ |
||||
|
const emit = defineEmits(['changeArea', 'reset', 'reload']); |
||||
|
|
||||
|
// 照明区域业务 ======================================================================= |
||||
|
|
||||
|
// 按钮区展开与收起状态 |
||||
|
const showAllButtonsArea = ref(false); |
||||
|
// 被选中的分区 默认为1 用于选中样式渲染 |
||||
|
const selectedButton = ref<string | undefined>('1'); |
||||
|
|
||||
|
// 分区切换 |
||||
|
const changeArea = (button: any) => { |
||||
|
// 当前选中按钮 |
||||
|
selectedButton.value = button.id; |
||||
|
// 设置当前选中的回路 |
||||
|
buttons2.value = button.childList; |
||||
|
// 重置按钮状态 |
||||
|
emit('reset'); |
||||
|
// 设置选中按钮状态 |
||||
|
button.selected = true; |
||||
|
// 改变俯视图样式 |
||||
|
emit('changeArea', button.id); |
||||
|
// 当前选中回路 - 置空 |
||||
|
resetMode(); |
||||
|
// 最近交互过的按钮 - 置空 |
||||
|
handleButton.value = ''; |
||||
|
}; |
||||
|
// 默认最多展示8个按钮 |
||||
|
const limitedButtons1 = computed(() => props.treeData.slice(0, 8)); |
||||
|
|
||||
|
// 照明回路业务 ====================================================================== |
||||
|
|
||||
|
// 最近交互过的按钮id,用于禁用和启用 |
||||
|
const handleButton = ref(''); |
||||
|
// 开关启用/禁用状态 |
||||
|
const isPlanEnabled2 = ref(true); |
||||
|
// 开关启用/禁用切换事件 |
||||
|
const togglePlan2 = () => { |
||||
|
// 如果未交互任何按钮 |
||||
|
if (handleButton.value == '') { |
||||
|
return message.info('请选择照明回路'); |
||||
|
} |
||||
|
// 获取最近交互过的按钮 |
||||
|
const btn: any = buttons2.value.find((button: any) => button.id === handleButton.value); |
||||
|
let panel = +!btn.ctrlStatus; |
||||
|
isLoading.value = true; |
||||
|
http |
||||
|
.get(lightingManage.setDisable, { |
||||
|
deviceGroup: btn.dataCode, |
||||
|
panel, |
||||
|
projectId: state.projectId, |
||||
|
siteId: state.siteId, |
||||
|
}) |
||||
|
.then((res) => { |
||||
|
if (res.msg === 'success') { |
||||
|
// 确认成功后 - 按钮文本取反 - 按钮布尔值修改 - 关闭loading |
||||
|
isPlanEnabled2.value = !isPlanEnabled2.value; |
||||
|
btn.ctrlStatus = panel; |
||||
|
isLoading.value = false; |
||||
|
} else { |
||||
|
isLoading.value = false; |
||||
|
} |
||||
|
}) |
||||
|
.catch(() => { |
||||
|
isLoading.value = false; |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
// 是否单选,状态 |
||||
|
let singleSelection = ref(true); |
||||
|
// 多选与单选切换事件 |
||||
|
const selectAll = () => { |
||||
|
selectAllCheckbox.value = false; |
||||
|
// 切换时清空当前所有选项 |
||||
|
buttons2.value.forEach((button: any) => { |
||||
|
button.selected = false; |
||||
|
}); |
||||
|
singleSelection.value = !singleSelection.value; |
||||
|
// 当前选中回路 - 置空 |
||||
|
resetMode(); |
||||
|
// 改变俯视图样式 |
||||
|
emit('changeArea', [selectedButton.value]); |
||||
|
}; |
||||
|
|
||||
|
// 全选状态 |
||||
|
const selectAllCheckbox = ref(false); |
||||
|
// 全选切换事件(switch) |
||||
|
const toggleAllSelection = () => { |
||||
|
let arr = [selectedButton.value]; |
||||
|
// 全选 |
||||
|
if (selectAllCheckbox.value) { |
||||
|
buttons2.value.forEach((item: any, index: number) => { |
||||
|
// 全选时,默认展示第一条回路的模式-场景按钮 |
||||
|
if (index == 0) { |
||||
|
thisButton2.value = item; |
||||
|
} |
||||
|
item.selected = true; |
||||
|
arr.push(item.id); |
||||
|
}); |
||||
|
// 全不选 |
||||
|
} else { |
||||
|
buttons2.value.forEach((item: any) => { |
||||
|
item.selected = false; |
||||
|
}); |
||||
|
// 全不选时,隐藏 模式 与 场景 按钮 |
||||
|
resetMode(); |
||||
|
} |
||||
|
emit('changeArea', arr); |
||||
|
}; |
||||
|
|
||||
|
// 储存当前选中的回路 |
||||
|
const thisButton2 = ref({ |
||||
|
dataCode: '', |
||||
|
name: '', |
||||
|
treePid: '', |
||||
|
childList: [], |
||||
|
}); |
||||
|
// 照明回路的按钮切换 |
||||
|
const changeLine = (button: any) => { |
||||
|
console.log(button, 'button2mmmm'); |
||||
|
// 存储一次按钮ID,用于禁用/启用交互 |
||||
|
handleButton.value = button.id; |
||||
|
// 根据按钮状态,展示禁用/启用按钮文本 |
||||
|
if (button.ctrlStatus) { |
||||
|
isPlanEnabled2.value = true; |
||||
|
} else { |
||||
|
isPlanEnabled2.value = false; |
||||
|
} |
||||
|
// 选择时反控俯视图 |
||||
|
let level1 = selectedButton.value; |
||||
|
let level2 = button.id; |
||||
|
// 单选模式,需将所有其他回路设为false |
||||
|
if (singleSelection.value) { |
||||
|
buttons2.value.forEach((item: any) => { |
||||
|
item.selected = false; |
||||
|
}); |
||||
|
button.selected = !button.selected; |
||||
|
emit('changeArea', [level1, level2]); |
||||
|
} else { |
||||
|
// 多选模式传值 |
||||
|
button.selected = !button.selected; |
||||
|
const arr = [level1]; |
||||
|
buttons2.value.forEach((item: any) => { |
||||
|
if (item.selected) { |
||||
|
arr.push(item.id); |
||||
|
} |
||||
|
}); |
||||
|
emit('changeArea', arr); |
||||
|
} |
||||
|
// 用于展示控制模式 |
||||
|
if (button.selected) { |
||||
|
// 发生了选中事件 |
||||
|
thisButton2.value = button; |
||||
|
// 获得启动模式 |
||||
|
if (button.childList) { |
||||
|
thisButton3.value = button.childList.find((item: any) => { |
||||
|
if (item.selectAble) { |
||||
|
return item.childList ? item.childList : []; |
||||
|
} |
||||
|
}); |
||||
|
} else { |
||||
|
thisButton3.value = { childList: [] }; |
||||
|
} |
||||
|
} else { |
||||
|
// 未发生选中 或 多选的其中一个按钮被取消 |
||||
|
resetMode(); |
||||
|
} |
||||
|
console.log(thisButton2, 'button'); |
||||
|
}; |
||||
|
|
||||
|
// 照明回路所有按钮 |
||||
|
const buttons2 = ref([]); |
||||
|
// 按钮区展开与收起状态 |
||||
|
const showAllButtons = ref(false); |
||||
|
// 默认最多展示8个按钮 |
||||
|
const limitedButtons2 = computed(() => buttons2.value.slice(0, 8)); |
||||
|
|
||||
|
// 控制模式业务 ==================================================================== |
||||
|
|
||||
|
const thisButton3 = ref({ |
||||
|
childList: [], |
||||
|
}); |
||||
|
// 控制模式 - 按钮切换 |
||||
|
const selectButton3 = (button3: any) => { |
||||
|
console.log(button3); |
||||
|
if (!button3.selectAble) { |
||||
|
return message.warning(`${button3.name} 暂不支持`); |
||||
|
} |
||||
|
thisButton3.value = button3; |
||||
|
}; |
||||
|
|
||||
|
// 控制场景业务 ===================================================================== |
||||
|
|
||||
|
// 控制场景 - 按钮切换 |
||||
|
const selectButton4 = (button4: any) => { |
||||
|
const after = button4.dataCode; |
||||
|
let before = ''; |
||||
|
// 如果是多选模式 |
||||
|
if (!singleSelection.value) { |
||||
|
// 操作线路总数 |
||||
|
let sum = 0; |
||||
|
// 没有这个选项的线路 |
||||
|
let nofind = 0; |
||||
|
// 修改无效的数量 |
||||
|
let checked = 0; |
||||
|
// 可修改的数量 |
||||
|
let changed = 0; |
||||
|
buttons2.value.forEach((item: any) => { |
||||
|
// 取出当前选中的值 before |
||||
|
if (item.childList) { |
||||
|
let mode = item.childList.find((lv3: any) => { |
||||
|
return lv3.selectAble; |
||||
|
}); |
||||
|
if (mode.childList) { |
||||
|
mode.childList.forEach((mode: any) => { |
||||
|
// 取出当前选中的模式 |
||||
|
if (mode.executeStatus.value == 1) { |
||||
|
before = mode.dataCode; |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
// 查看当前回路,哪些被选中 |
||||
|
if (item.selected) { |
||||
|
item.childList.forEach((v: any) => { |
||||
|
if (v.childList) { |
||||
|
v.childList.forEach((v1: any) => { |
||||
|
// 查看是否包含当前要修改的值 |
||||
|
if (v1.dataCode == after) { |
||||
|
sum += 1; |
||||
|
// 已经被选中,无需修改的内容 |
||||
|
if (before == after) { |
||||
|
return (checked += 1); |
||||
|
// 修改 |
||||
|
} else { |
||||
|
alert(v.treePid + '---' + before + '---' + after); |
||||
|
changed += 1; |
||||
|
resetScene(item.childList); |
||||
|
changeScene(v1, before, after); |
||||
|
v1.executeStatus.value = 1; |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
alert(`共修改${sum}条,${checked}条无需修改,${nofind}条不具有该选项,${changed}条生效`); |
||||
|
// 如果是单选模式 |
||||
|
} else { |
||||
|
// 如果按钮已经被选择 |
||||
|
if (button4.executeStatus.value == 1) { |
||||
|
return message.info('未产生实际修改'); |
||||
|
} else { |
||||
|
// 获得线路当前的场景 |
||||
|
let before; |
||||
|
thisButton2.value.childList.find((item: any) => { |
||||
|
if (item.childList) { |
||||
|
item.childList.forEach((i: any) => { |
||||
|
if (i.executeStatus.value == 1) { |
||||
|
return (before = i.dataCode); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
// 移除选中场景 |
||||
|
resetScene(thisButton2.value.childList); |
||||
|
changeScene(button4, before, after); |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
// 切换场景前,需要把其他场景移除 |
||||
|
const resetScene = (list: any) => { |
||||
|
list.forEach((item: any) => { |
||||
|
if (item.childList) { |
||||
|
item.childList.forEach((i: any) => { |
||||
|
i.executeStatus.value = 0; |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
/** 控制场景 - 按钮切换通用方法(单选 & 多选) |
||||
|
* @param button 当前被选中的场景(单选 & 多选) |
||||
|
* @param before 当前回路场景的初始值(撤回时需使用) |
||||
|
*/ |
||||
|
const changeScene = (button: any, before: string | undefined, after: string) => { |
||||
|
console.log(changeList.value, 'vvvvvvvvvvvvvv-----', button, 'bbbbbbbb---------'); |
||||
|
// 通过分组ID,查询之前是否修改过 |
||||
|
const result = changeList.value.find((item: any, index: number) => { |
||||
|
item.index = index; |
||||
|
return item.deviceGroup == button.treePid.split('_')[0]; |
||||
|
}); |
||||
|
console.log(result, 'rrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr'); |
||||
|
// 如果已产生过修改 |
||||
|
if (result) { |
||||
|
// 改回了原有的值,则从数组中移除 |
||||
|
if (after == result.before) { |
||||
|
changeList.value.splice(result.index, 1); |
||||
|
// 修改为新值,则只修改场景 |
||||
|
} else { |
||||
|
result.scene = after; |
||||
|
// 临时flag,用后移除 |
||||
|
delete result.index; |
||||
|
} |
||||
|
// 如果未产生过修改 |
||||
|
} else { |
||||
|
changeList.value.push({ |
||||
|
// 回路 |
||||
|
deviceGroup: thisButton2.value.dataCode, |
||||
|
deviceGroupName: thisButton2.value.name, |
||||
|
// 分区 |
||||
|
region: thisButton2.value.treePid, |
||||
|
regionName: '', |
||||
|
// 修改前 |
||||
|
before, |
||||
|
scene: after, |
||||
|
}); |
||||
|
} |
||||
|
button.executeStatus.value = 1; |
||||
|
}; |
||||
|
|
||||
|
// 底部按钮区 ====================================================================== |
||||
|
|
||||
|
const isLoading = ref(false); |
||||
|
// 执行按钮loading |
||||
|
const buttonLoading = ref(false); |
||||
|
// 刷新 |
||||
|
const refresh = () => { |
||||
|
// 关闭执行弹窗 |
||||
|
executeVisible.value = false; |
||||
|
// 设置当前选中的序列 |
||||
|
selectedButton.value = '1'; |
||||
|
// 重置选中样式 和 按钮选中状态 |
||||
|
emit('changeArea', ['1']); |
||||
|
emit('reset'); |
||||
|
// 将所有修改改回 |
||||
|
changeList.value.forEach((item: any) => { |
||||
|
resetChangeList(item); |
||||
|
}); |
||||
|
changeList.value = []; |
||||
|
// 默认选择第一个楼层 |
||||
|
let data = props.treeData[0]; |
||||
|
// 默认选中 |
||||
|
data.selected = true; |
||||
|
// 默认选中 1-1 分区 回路 |
||||
|
buttons2.value = data.childList; |
||||
|
// 将选中线路重置 |
||||
|
resetMode(); |
||||
|
}; |
||||
|
// 刷新时,将已修改的值改回 |
||||
|
const resetChangeList = (item: any) => { |
||||
|
props.treeData.find((v: any) => { |
||||
|
if (item.region == v.dataCode) { |
||||
|
v.childList.find((v1: any) => { |
||||
|
// 找到被修改过的线路 |
||||
|
if (item.deviceGroup == v1.dataCode) { |
||||
|
v1.childList.forEach((v2: any) => { |
||||
|
if (v2.childList) { |
||||
|
v2.childList.forEach((v3: any) => { |
||||
|
// 将新值移除 |
||||
|
if (item.scene == v3.dataCode) { |
||||
|
v3.executeStatus.value = 0; |
||||
|
} |
||||
|
// 旧值选中 |
||||
|
if (item.before == v3.dataCode) { |
||||
|
v3.executeStatus.value = 1; |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
}; |
||||
|
// 右下角的执行事件 |
||||
|
const showModal = () => { |
||||
|
if (!changeList.value.length) { |
||||
|
return message.info('未产生任何修改'); |
||||
|
} |
||||
|
buttonLoading.value = true; |
||||
|
http |
||||
|
.post(airConditionControl.getChangeList, { |
||||
|
sceneList: changeList.value, |
||||
|
lockList: [], |
||||
|
projectId: state.projectId, |
||||
|
siteId: state.siteId, |
||||
|
}) |
||||
|
.then((res) => { |
||||
|
if (res.msg === 'success') { |
||||
|
diffList.value = res.data; |
||||
|
executeVisible.value = true; |
||||
|
} else { |
||||
|
message.warning('获取修改内容失败'); |
||||
|
} |
||||
|
buttonLoading.value = false; |
||||
|
}) |
||||
|
.catch(() => { |
||||
|
buttonLoading.value = false; |
||||
|
}); |
||||
|
}; |
||||
|
// 通用取消 |
||||
|
const changeCancel = () => {}; |
||||
|
|
||||
|
// 内侧弹窗 ======================================================================== |
||||
|
|
||||
|
// 内侧弹窗显隐 |
||||
|
const executeVisible = ref<boolean>(false); |
||||
|
// 修改模式 需要向后端提交的内容 |
||||
|
const changeList: any = ref([]); |
||||
|
// 展示修改前后差异的内容 |
||||
|
const diffList = ref([]); |
||||
|
//撤销 |
||||
|
const delBtn = (obj: any) => { |
||||
|
// 将treeData对应回路的数据改回,数据以后端为准 |
||||
|
obj.scene = obj.stateAfter.scene.value; |
||||
|
obj.before = obj.stateBefore.scene.value; |
||||
|
resetChangeList(obj); |
||||
|
// 将 changeList 与 diffList 中记录的修改移除 (排除极端情况) |
||||
|
changeList.value = changeList.value.filter((item: any) => { |
||||
|
return item.deviceGroup !== obj.deviceGroup; |
||||
|
}); |
||||
|
diffList.value = diffList.value.filter((item: any) => { |
||||
|
return item.deviceGroup !== obj.deviceGroup; |
||||
|
}); |
||||
|
console.log(changeList, 'changeList'); |
||||
|
console.log(diffList, 'diffList'); |
||||
|
// 如果移除后不再有修改内容,则隐藏弹出框 |
||||
|
if (changeList.value.length == 0) { |
||||
|
executeVisible.value = false; |
||||
|
} |
||||
|
}; |
||||
|
// 提交本次修改 |
||||
|
const submitChangeList = () => { |
||||
|
http |
||||
|
.post(airConditionControl.submitChangeList, { |
||||
|
sceneList: changeList.value, |
||||
|
lockList: [], |
||||
|
projectId: state.projectId, |
||||
|
siteId: state.siteId, |
||||
|
}) |
||||
|
.then((res) => { |
||||
|
emit('reload'); |
||||
|
refresh(); |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
// 其他业务 ======================================================================== |
||||
|
|
||||
|
// 将当前选择的回路置空 |
||||
|
const resetMode = () => { |
||||
|
thisButton2.value = { |
||||
|
dataCode: '', |
||||
|
name: '', |
||||
|
treePid: '', |
||||
|
childList: [], |
||||
|
}; |
||||
|
}; |
||||
|
|
||||
|
// 向外暴露方法 |
||||
|
defineExpose({ |
||||
|
// 分区切换 |
||||
|
changeArea, |
||||
|
// 回路切换 |
||||
|
changeLine, |
||||
|
}); |
||||
|
</script> |
||||
|
<style lang="less" scoped> |
||||
|
@import './dialogStyle.less'; |
||||
|
@import './tabs1.less'; |
||||
|
</style> |
@ -0,0 +1,318 @@ |
|||||
|
<template> |
||||
|
<div class="div-add"> |
||||
|
<button class="add" @click="addModal">添加</button> |
||||
|
<button class="add" style="margin-left: 20px" @click="sendTable">执行</button> |
||||
|
</div> |
||||
|
<div class="buttons"> |
||||
|
<span style="color: red; padding-top: 20px">*以下修改需执行后生效</span> |
||||
|
<div class="plans"> |
||||
|
<button class="plan enabled" style="margin-right: 10px" @click="togglePlan(1)"> |
||||
|
计划启用 |
||||
|
</button> |
||||
|
<button class="plan disabled" @click="togglePlan(3)"> 计划禁用 </button> |
||||
|
</div> |
||||
|
</div> |
||||
|
<table class="custom-table table1"> |
||||
|
<thead> |
||||
|
<tr :style="{ background: 'rgba(35,45,69)' }"> |
||||
|
<th>序号</th> |
||||
|
<th>执行时间</th> |
||||
|
<th>计划名称</th> |
||||
|
<th>状态</th> |
||||
|
<th>操作</th> |
||||
|
</tr> |
||||
|
</thead> |
||||
|
<tbody> |
||||
|
<tr v-for="(row, index) in dataSource" :key="index"> |
||||
|
<td>{{ index + 1 }}</td> |
||||
|
<td>{{ row.startTime }}</td> |
||||
|
<td>{{ row.planName }}</td> |
||||
|
<td> |
||||
|
<button |
||||
|
:style="{ |
||||
|
'font-size': '12px', |
||||
|
width: '5em', |
||||
|
background: 'rgb(47, 47, 47)', |
||||
|
color: setStateColor(row.executeStatus.value), |
||||
|
border: '1px solid', |
||||
|
}"> |
||||
|
{{ setStateText(row.executeStatus.value) }} |
||||
|
</button> |
||||
|
</td> |
||||
|
<td> |
||||
|
<div class="tabReboot" @click="startPlan(row)">启用</div> |
||||
|
<a-popconfirm |
||||
|
title="此操作将移除该数据" |
||||
|
ok-text="确定" |
||||
|
cancel-text="取消" |
||||
|
@confirm="deletePlan(row)"> |
||||
|
<div class="tabDelete">删除</div> |
||||
|
</a-popconfirm> |
||||
|
</td> |
||||
|
</tr> |
||||
|
</tbody> |
||||
|
</table> |
||||
|
<div class="out-dialog" v-if="addVisible"> |
||||
|
<div class="content" v-if="addVisible"> |
||||
|
<div class="div-operation"></div> |
||||
|
<span class="text-operation">计划库</span> |
||||
|
</div> |
||||
|
<div style="margin-top: 20px"> |
||||
|
<a-transfer |
||||
|
v-model:target-keys="targetKeys" |
||||
|
:data-source="transferData" |
||||
|
show-search |
||||
|
:filter-option="filterOption" |
||||
|
:render="(item: any) => item.title" |
||||
|
@change="handleChange" |
||||
|
:style="{ color: 'rgba(255,255,255,1)' }" |
||||
|
@search="handleSearch" |
||||
|
:listStyle="{ border: '2px solid rgba(25,74,125,1)', height: 'calc(100vh - 200px)' }" /> |
||||
|
</div> |
||||
|
<div style="width: 100%; height: 60px"></div> |
||||
|
<div class="button-box"> |
||||
|
<button class="cancel" @click="addVisible = false">取消</button> |
||||
|
<button class="execute" @click="sendPlan">确定</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script setup lang="ts"> |
||||
|
import { ref, onMounted } from 'vue'; |
||||
|
// 请求 |
||||
|
import { http } from '/nerv-lib/util/http'; |
||||
|
import { airConditionControl } from '/@/api/airConditionControlSystem'; |
||||
|
import { message } from 'ant-design-vue'; |
||||
|
import { getEnum } from '/@/api'; |
||||
|
// 全局变量 |
||||
|
import { items } from '/@/store/item'; |
||||
|
|
||||
|
// 初始化 =========================================================== |
||||
|
|
||||
|
onMounted(() => { |
||||
|
getStateEnum(); |
||||
|
// 计划表格 |
||||
|
getTable(); |
||||
|
// 穿梭框原始数据 |
||||
|
getLeftPlan(); |
||||
|
}); |
||||
|
// 全局变量 |
||||
|
const state = items(); |
||||
|
const getStateEnum = async () => { |
||||
|
let enumData = await getEnum({ params: { enumType: 'PlanExecuteStatus' } }); |
||||
|
stateList.value = enumData.data; |
||||
|
}; |
||||
|
|
||||
|
// tab页部分 ======================================================== |
||||
|
|
||||
|
// 状态枚举 |
||||
|
const stateList = ref([]); |
||||
|
// 设置枚举的颜色 与 文本 |
||||
|
const setStateColor = (state: number) => { |
||||
|
if (state == 0) { |
||||
|
return '#ccc'; |
||||
|
} else if (state == 1) { |
||||
|
return 'orange'; |
||||
|
} else if (state == 2) { |
||||
|
return 'rgb(57, 215, 187)'; |
||||
|
} else if (state == 3) { |
||||
|
return 'rgb(255, 0, 0)'; |
||||
|
} |
||||
|
}; |
||||
|
// 设置枚举的文本 |
||||
|
const setStateText = (state: number) => { |
||||
|
const res = stateList.value.find((item) => { |
||||
|
return item.value == state; |
||||
|
}); |
||||
|
return res.label; |
||||
|
}; |
||||
|
// 计划启用/禁用事件 |
||||
|
const togglePlan = (state: number) => { |
||||
|
dataSource.value.forEach((item: any) => { |
||||
|
item.executeStatus.value = state; |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
// 表格数据 |
||||
|
const dataSource = ref([]); |
||||
|
// 获得表格数据 |
||||
|
const getTable = () => { |
||||
|
http |
||||
|
.get(airConditionControl.getTableData, { projectId: state.projectId, siteId: state.siteId }) |
||||
|
.then((res) => { |
||||
|
dataSource.value = res.data; |
||||
|
}); |
||||
|
}; |
||||
|
// 删除表格中的计划(将当前任意状态,修改为未启用 =0) |
||||
|
const deletePlan = (row: any) => { |
||||
|
row.executeStatus.value = 0; |
||||
|
}; |
||||
|
// 重启表格中的计划(将当前任意状态,修改为待执行 = 1) |
||||
|
const startPlan = (row: any) => { |
||||
|
if (row.executeStatus.value == 1) { |
||||
|
return message.info('该数据已是待执行状态,无需再次修改'); |
||||
|
} |
||||
|
row.executeStatus.value = 1; |
||||
|
}; |
||||
|
// 将对表格的修改统一发送 |
||||
|
const sendTable = () => { |
||||
|
http.post(airConditionControl.submitTableData, dataSource.value).then((res) => { |
||||
|
message.success('操作成功'); |
||||
|
getTable(); |
||||
|
getLeftPlan(); |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
// tab页弹窗部分 ==================================================== |
||||
|
|
||||
|
// 添加弹窗控制变量 |
||||
|
const addVisible = ref(false); |
||||
|
// 打开弹窗 |
||||
|
const addModal = () => { |
||||
|
addVisible.value = true; |
||||
|
}; |
||||
|
|
||||
|
// 穿梭框部分 ======================================================= |
||||
|
|
||||
|
// 穿梭框数据 |
||||
|
const transferData = ref([]) as any; |
||||
|
// 获得穿梭框原始数据 |
||||
|
const getLeftPlan = () => { |
||||
|
http |
||||
|
.get(airConditionControl.getTransData, { projectId: state.projectId, siteId: state.siteId }) |
||||
|
.then((res) => { |
||||
|
let arr = []; |
||||
|
res.data.forEach((item: any) => { |
||||
|
arr.push({ |
||||
|
key: item.id, |
||||
|
title: item.planName, |
||||
|
}); |
||||
|
}); |
||||
|
transferData.value = arr; |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
const handleChange = (keys: string[], direction: string, moveKeys: string[]) => { |
||||
|
console.log(keys, direction, moveKeys); |
||||
|
}; |
||||
|
const handleSearch = (dir: string, value: string) => { |
||||
|
console.log('search:', dir, value); |
||||
|
}; |
||||
|
// 穿梭框选中数据 |
||||
|
const targetKeys = ref<string[]>([]); |
||||
|
// 将穿梭框选中的计划提交 |
||||
|
const sendPlan = () => { |
||||
|
if (targetKeys.value.length < 1) { |
||||
|
return message.info('没有选择任何计划'); |
||||
|
} |
||||
|
http.post(airConditionControl.submitTransData, targetKeys.value).then(() => { |
||||
|
message.success('添加成功'); |
||||
|
// 如果发送成功,则刷新表格 |
||||
|
getTable(); |
||||
|
getLeftPlan(); |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
const filterOption = (inputValue: string, option: any) => { |
||||
|
return option.description.indexOf(inputValue) > -1; |
||||
|
}; |
||||
|
</script> |
||||
|
<style lang="less" scoped> |
||||
|
@import './dialogStyle.less'; |
||||
|
.buttons { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
.plan { |
||||
|
border: none; |
||||
|
font-size: 14px; |
||||
|
font-weight: 400; |
||||
|
border-radius: 5px; |
||||
|
width: 88px; |
||||
|
height: 32px; |
||||
|
color: white; |
||||
|
cursor: pointer; |
||||
|
margin: 15px 0; |
||||
|
vertical-align: middle; |
||||
|
} |
||||
|
.plan.enabled { |
||||
|
background: linear-gradient(180deg, rgba(103, 222, 0, 1) 0%, rgba(0, 181, 6, 1) 100%); |
||||
|
} |
||||
|
.plan.disabled { |
||||
|
background-color: red; |
||||
|
} |
||||
|
.plan:disabled { |
||||
|
cursor: not-allowed; |
||||
|
} |
||||
|
} |
||||
|
// 右下角添加按钮 |
||||
|
.div-add { |
||||
|
height: 64px; |
||||
|
display: flex; |
||||
|
justify-content: flex-end; |
||||
|
align-items: center; |
||||
|
position: fixed; |
||||
|
bottom: 0; |
||||
|
right: 0; |
||||
|
margin-right: 10px; |
||||
|
.add { |
||||
|
width: 74px; |
||||
|
height: 40px; |
||||
|
opacity: 1; |
||||
|
border-radius: 4px; |
||||
|
background: rgba(67, 136, 251, 1); |
||||
|
border: rgba(67, 136, 251, 1); |
||||
|
font-size: 14px; |
||||
|
font-weight: 400; |
||||
|
color: rgba(255, 255, 255, 1); |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
} |
||||
|
// 表格 |
||||
|
.custom-table { |
||||
|
border-collapse: collapse; |
||||
|
width: 416px; |
||||
|
height: 40px; |
||||
|
color: rgba(255, 255, 255, 1); |
||||
|
} |
||||
|
.custom-table th, |
||||
|
.custom-table td { |
||||
|
border: 1px solid rgba(163, 192, 243, 1); |
||||
|
text-align: left; |
||||
|
padding: 8px; |
||||
|
text-align: center; |
||||
|
} |
||||
|
.table1 { |
||||
|
width: 100%; |
||||
|
border: 1px solid rgba(255, 255, 255); |
||||
|
border-radius: 5px; |
||||
|
background: rgba(255, 255, 255, 0.1); |
||||
|
// 表格中的操作按钮 |
||||
|
.tabReboot, |
||||
|
.tabDelete { |
||||
|
border: none; |
||||
|
display: inline-block; |
||||
|
background-color: rgba(0, 0, 0, 0); |
||||
|
font-size: 14px; |
||||
|
font-weight: 400; |
||||
|
letter-spacing: 0; |
||||
|
line-height: 20px; |
||||
|
color: rgba(67, 136, 251, 1); |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
.tabReboot { |
||||
|
margin-right: 8px; |
||||
|
} |
||||
|
.tabReboot::active { |
||||
|
color: white !important; |
||||
|
} |
||||
|
.tabDelete::active { |
||||
|
color: white; |
||||
|
} |
||||
|
} |
||||
|
::v-deep(.ant-transfer) { |
||||
|
// 屏蔽自带的hover效果 |
||||
|
.ant-transfer-list-content-item:hover { |
||||
|
background: black; |
||||
|
} |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,251 @@ |
|||||
|
<template> |
||||
|
<table class="custom-table table1"> |
||||
|
<thead> |
||||
|
<tr :style="{ background: 'rgba(35,45,69)' }"> |
||||
|
<th>序号</th> |
||||
|
<th>执行时间</th> |
||||
|
<th>操作内容</th> |
||||
|
<th>操作人</th> |
||||
|
</tr> |
||||
|
</thead> |
||||
|
<tbody> |
||||
|
<tr |
||||
|
v-for="(row, index) in dataSource1" |
||||
|
:key="index" |
||||
|
@click="handleRowClick(row.key)" |
||||
|
:class="row.key === trIndex ? 'isTrIndex' : ''"> |
||||
|
<td>{{ row.key }}</td> |
||||
|
<td>{{ row.data }}</td> |
||||
|
<td>{{ row.planName }}</td> |
||||
|
<td>{{ row.status }}</td> |
||||
|
</tr> |
||||
|
</tbody> |
||||
|
</table> |
||||
|
|
||||
|
<div class="out-dialog" :class="{ showDialog: logModalVisible }" v-if="logModalVisible"> |
||||
|
<div class="content" v-if="logModalVisible"> |
||||
|
<div> |
||||
|
<div class="div-operation"></div> |
||||
|
<span class="text-operation">操作日志 </span> |
||||
|
</div> |
||||
|
<div class="j-box" v-for="item in cxList" :key="item.id"> |
||||
|
<div class="journal" style="margin-top: 20px"> |
||||
|
<div class="imgText"> |
||||
|
<div class="zjzm"> |
||||
|
<img class="title-img" src="/asset/image//bulbLogo/21961.png" alt="" /> |
||||
|
<span |
||||
|
class="title-text" |
||||
|
style="font-size: 20px; font-weight: 500; color: rgba(255, 255, 255, 1)" |
||||
|
>{{ item.name }}</span |
||||
|
> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="btn-box"> |
||||
|
<div class="btn-item"> |
||||
|
<div class="left">控制模式</div> |
||||
|
<div class="right"> |
||||
|
<span>手动</span> |
||||
|
<img src="/asset/image/bulbLogo/22406.png" alt="" /> |
||||
|
<span>自动</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="btn-item"> |
||||
|
<div class="left"> 亮度 </div> |
||||
|
<div class="right"> |
||||
|
<span>100lux</span> |
||||
|
<img src="/asset/image/bulbLogo/22406.png" alt="" /> |
||||
|
<span>30lux</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="btn-item"> |
||||
|
<div class="left"> 控制场景 </div> |
||||
|
<div class="right"> |
||||
|
<span>手动</span> |
||||
|
<img src="/asset/image/bulbLogo/22406.png" alt="" /> |
||||
|
<span>自动</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="btn-item"> |
||||
|
<div class="left"> 色温 </div> |
||||
|
<div class="right"> |
||||
|
<span>4000k</span> |
||||
|
<img src="/asset/image/bulbLogo/22406.png" alt="" /> |
||||
|
<span>3800k</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div style="height: 60px"></div> |
||||
|
<div class="button-box"> |
||||
|
<button class="cancel" @click="logModalVisible = false">取消</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="div-add"> |
||||
|
<button class="add">刷新</button> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script setup lang="ts"> |
||||
|
import { ref, onMounted } from 'vue'; |
||||
|
|
||||
|
// 初始化 |
||||
|
onMounted(() => {}); |
||||
|
|
||||
|
// 与父组件的交互 =================================================================== |
||||
|
const props = defineProps({ |
||||
|
// 分区结构(照明区域 > 照明回路) |
||||
|
treeData: { |
||||
|
type: Array, |
||||
|
}, |
||||
|
}); |
||||
|
const emit = defineEmits(['changeArea']); |
||||
|
|
||||
|
// 表格数据 |
||||
|
const dataSource1 = ref([ |
||||
|
{ |
||||
|
key: '1', |
||||
|
data: '2024-05-01', |
||||
|
planName: '计划再开', |
||||
|
status: '张三111', |
||||
|
}, |
||||
|
{ |
||||
|
key: '2', |
||||
|
data: '2024-05-01', |
||||
|
planName: '检修模式', |
||||
|
status: '李四12', |
||||
|
}, |
||||
|
{ |
||||
|
key: '3', |
||||
|
data: '2024-05-01', |
||||
|
planName: '设备变更', |
||||
|
status: '王五33', |
||||
|
}, |
||||
|
]); |
||||
|
const cxList = ref([ |
||||
|
{ |
||||
|
id: '1', |
||||
|
name: '站厅照明 1区', |
||||
|
manual: '手动', |
||||
|
automatic: '自动', |
||||
|
brightness: '100lux', |
||||
|
brightness2: '30lux', |
||||
|
manual2: '手动', |
||||
|
automatic2: '自动', |
||||
|
brightness3: '4000k', |
||||
|
brightness4: '3800k', |
||||
|
}, |
||||
|
{ |
||||
|
id: '2', |
||||
|
name: '站厅照明 2区', |
||||
|
manual: '手动', |
||||
|
automatic: '自动', |
||||
|
brightness: '100lux', |
||||
|
brightness2: '30lux', |
||||
|
manual2: '手动', |
||||
|
automatic2: '自动', |
||||
|
brightness3: '4000k', |
||||
|
brightness4: '3800k', |
||||
|
}, |
||||
|
{ |
||||
|
id: '3', |
||||
|
name: '站厅照明 3区', |
||||
|
manual: '手动', |
||||
|
automatic: '自动', |
||||
|
brightness: '100lux', |
||||
|
brightness2: '30lux', |
||||
|
manual2: '手动', |
||||
|
automatic2: '自动', |
||||
|
brightness3: '4000k', |
||||
|
brightness4: '3800k', |
||||
|
}, |
||||
|
]); |
||||
|
let trIndex = ref('-1'); |
||||
|
const logModalVisible = ref(false); |
||||
|
const handleRowClick = (index: any) => { |
||||
|
trIndex.value = index; |
||||
|
if (index === trIndex.value) { |
||||
|
console.log('tri'); |
||||
|
} |
||||
|
// 显示模态框 |
||||
|
logModalVisible.value = true; |
||||
|
}; |
||||
|
</script> |
||||
|
<style lang="less" scoped> |
||||
|
@import './dialogStyle.less'; |
||||
|
|
||||
|
// 右下角添加按钮 |
||||
|
.div-add { |
||||
|
height: 64px; |
||||
|
display: flex; |
||||
|
justify-content: flex-end; |
||||
|
align-items: center; |
||||
|
position: fixed; |
||||
|
bottom: 0; |
||||
|
right: 0; |
||||
|
margin-right: 20px; |
||||
|
.add { |
||||
|
width: 74px; |
||||
|
height: 40px; |
||||
|
opacity: 1; |
||||
|
border-radius: 4px; |
||||
|
background: rgba(67, 136, 251, 1); |
||||
|
border: rgba(67, 136, 251, 1); |
||||
|
font-size: 14px; |
||||
|
font-weight: 400; |
||||
|
color: rgba(255, 255, 255, 1); |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 表格 |
||||
|
.custom-table { |
||||
|
border-collapse: collapse; |
||||
|
width: 416px; |
||||
|
height: 60px; |
||||
|
color: rgba(255, 255, 255, 1); |
||||
|
} |
||||
|
|
||||
|
.custom-table th, |
||||
|
.custom-table td { |
||||
|
border: 1px solid rgba(163, 192, 243, 1); |
||||
|
text-align: left; |
||||
|
padding: 8px; |
||||
|
text-align: center; |
||||
|
} |
||||
|
|
||||
|
.table1 { |
||||
|
margin-top: 20px; |
||||
|
width: 100%; |
||||
|
border: 1px solid rgba(255, 255, 255); |
||||
|
border-radius: 5px; |
||||
|
background: rgba(255, 255, 255, 0.1); |
||||
|
|
||||
|
.tabReboot, |
||||
|
.tabDelete { |
||||
|
border: none; |
||||
|
background-color: rgba(0, 0, 0, 0); |
||||
|
font-size: 14px; |
||||
|
font-weight: 400; |
||||
|
letter-spacing: 0; |
||||
|
line-height: 20px; |
||||
|
color: rgba(67, 136, 251, 1); |
||||
|
} |
||||
|
|
||||
|
.tabReboot { |
||||
|
margin-right: 8px; |
||||
|
} |
||||
|
|
||||
|
.isTrIndex { |
||||
|
background: rgba(67, 136, 251, 1); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
::v-deep(.ant-transfer) { |
||||
|
// 屏蔽自带的hover效果 |
||||
|
.ant-transfer-list-content-item:hover { |
||||
|
background: black; |
||||
|
} |
||||
|
} |
||||
|
</style> |
Loading…
Reference in new issue