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