Files
JY1.0/src/views/DeviceManagement/lightControl/index.vue
2026-05-21 09:46:02 +08:00

1410 lines
42 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="app-container">
<el-card class="main-card">
<!-- 楼层切换按钮 -->
<div class="floor-switcher">
<el-button :type="currentFloor === 1 ? 'primary' : ''" class="floor-btn" size="large" @click="switchFloor(1)">
<i class="el-icon-office-building"/>
一楼
</el-button>
<el-button :type="currentFloor === 2 ? 'primary' : ''" class="floor-btn" size="large" @click="switchFloor(2)">
<i class="el-icon-office-building"/>
二楼
</el-button>
<el-button :type="currentFloor === 3 ? 'primary' : ''" class="floor-btn" size="large" @click="switchFloor(3)">
<i class="el-icon-office-building"/>
三楼
</el-button>
</div>
<!-- 一楼 -->
<div v-show="currentFloor === 1" class="floor">
<!-- 一楼控制按钮 -->
<div class="floor-controls">
<el-button
:loading="loadingAll"
type="success"
class="control-btn"
size="large"
@click="confirmToggleAllLights(1, true)">
<i class="el-icon-open"/>
全部开启
</el-button>
<el-button
:loading="loadingAll"
type="danger"
class="control-btn"
size="large"
@click="confirmToggleAllLights(1, false)">
<i class="el-icon-turn-off"/>
全部关闭
</el-button>
<div class="floor-stats">
<span class="stat-item">开启: {{ getFloorStats(1).on }}</span>
<span class="stat-item">关闭: {{ getFloorStats(1).off }}</span>
<span class="stat-item">总数: {{ getFloorStats(1).total }}</span>
</div>
</div>
<div class="floor-content">
<!-- 左侧区域 -->
<div class="area-group left-group">
<div class="area-column group1">
<div class="light-area" @click="toggleLight('Tag_JY_Control_Light_177_1')">
<div :class="{ on: getLightStatus('Tag_JY_Control_Light_177_1') }" class="light-indicator">
<div v-if="getLightStatus('Tag_JY_Control_Light_177_1')" class="light-glow"/>
</div>
<span class="area-label">1F_177_1</span>
</div>
<div class="light-area" @click="toggleLight('Tag_JY_Control_Light_177_2')">
<div :class="{ on: getLightStatus('Tag_JY_Control_Light_177_2') }" class="light-indicator">
<div v-if="getLightStatus('Tag_JY_Control_Light_177_2')" class="light-glow"/>
</div>
<span class="area-label">1F_177_2</span>
</div>
</div>
<div class="area-column group2">
<div class="light-area" @click="toggleLight('Tag_JY_Control_Light_172_4')">
<div :class="{ on: getLightStatus('Tag_JY_Control_Light_172_4') }" class="light-indicator">
<div v-if="getLightStatus('Tag_JY_Control_Light_172_4')" class="light-glow"/>
</div>
<span class="area-label">1F_172_4</span>
</div>
<div class="light-area" @click="toggleLight('Tag_JY_Control_Light_172_3')">
<div :class="{ on: getLightStatus('Tag_JY_Control_Light_172_3') }" class="light-indicator">
<div v-if="getLightStatus('Tag_JY_Control_Light_172_3')" class="light-glow"/>
</div>
<span class="area-label">1F_172_3</span>
</div>
<div class="light-area" @click="toggleLight('Tag_JY_Control_Light_172_2')">
<div :class="{ on: getLightStatus('Tag_JY_Control_Light_172_2') }" class="light-indicator">
<div v-if="getLightStatus('Tag_JY_Control_Light_172_2')" class="light-glow"/>
</div>
<span class="area-label">1F_172_2</span>
</div>
<div class="light-area" @click="toggleLight('Tag_JY_Control_Light_172_1')">
<div :class="{ on: getLightStatus('Tag_JY_Control_Light_172_1') }" class="light-indicator">
<div v-if="getLightStatus('Tag_JY_Control_Light_172_1')" class="light-glow"/>
</div>
<span class="area-label">1F_172_1</span>
</div>
</div>
</div>
<!-- 中间区域 -->
<div class="area-group center-group">
<div v-for="(rowNum, rowIndex) in [176, 175, 174, 173, 171]" :key="rowIndex" class="area-row">
<div
v-for="(areaNum, areaIndex) in [4, 3, 2, 1]"
:key="areaIndex"
class="light-area"
@click="toggleLight('Tag_JY_Control_Light_' + rowNum + '_' + areaNum)">
<div
:class="{ on: getLightStatus('Tag_JY_Control_Light_' + rowNum + '_' + areaNum) }"
class="light-indicator">
<div v-if="getLightStatus('Tag_JY_Control_Light_' + rowNum + '_' + areaNum)" class="light-glow"/>
</div>
<span class="area-label">1F_{{ rowNum }}_{{ areaNum }}</span>
</div>
</div>
</div>
<!-- 右侧区域 -->
<div class="area-group right-group">
<div class="area-column">
<div
v-for="(areaNum, index) in [4, 3, 2, 1]"
:key="index"
class="light-area"
@click="toggleLight('Tag_JY_Control_Light_170_' + areaNum)">
<div :class="{ on: getLightStatus('Tag_JY_Control_Light_170_' + areaNum) }" class="light-indicator">
<div v-if="getLightStatus('Tag_JY_Control_Light_170_' + areaNum)" class="light-glow"/>
</div>
<span class="area-label">1F_170_{{ areaNum }}</span>
</div>
</div>
</div>
<div class="north-gate-label">
<div class="north-arrow"></div>
<div class="north-text">北大门</div>
</div>
</div>
</div>
<!-- 二楼 -->
<div v-show="currentFloor === 2" class="floor">
<!-- 二楼控制按钮 -->
<div class="floor-controls">
<el-button
:loading="loadingAll"
type="success"
class="control-btn"
size="large"
@click="confirmToggleAllLights(2, true)">
<i class="el-icon-open"/>
全部开启
</el-button>
<el-button
:loading="loadingAll"
type="danger"
class="control-btn"
size="large"
@click="confirmToggleAllLights(2, false)">
<i class="el-icon-turn-off"/>
全部关闭
</el-button>
<div class="floor-stats">
<span class="stat-item">开启: {{ getFloorStats(2).on }}</span>
<span class="stat-item">关闭: {{ getFloorStats(2).off }}</span>
<span class="stat-item">总数: {{ getFloorStats(2).total }}</span>
</div>
</div>
<div class="floor-content second-floor-content">
<!-- 二楼区域布局 -->
<div class="area-group second-floor-group">
<!-- 上方区域 - 铺满一行 -->
<div class="top-area">
<div
v-for="light in 4"
:key="light"
class="light-area"
@click="toggleLight('Tag_JY_Control_Light_185_' + light)">
<div :class="{ on: getLightStatus('Tag_JY_Control_Light_185_' + light) }" class="light-indicator">
<div v-if="getLightStatus('Tag_JY_Control_Light_185_' + light)" class="light-glow"/>
</div>
<span class="area-label">2F_185_{{ light }}</span>
</div>
</div>
<!-- 中间区域 -->
<div class="middle-area">
<!-- 中间左侧四分之一 -->
<div class="middle-left">
<div class="room-label">立库</div>
<div class="light-area" @click="toggleLight('Tag_JY_Control_Light_183_4')">
<div :class="{ on: getLightStatus('Tag_JY_Control_Light_183_4') }" class="light-indicator">
<div v-if="getLightStatus('Tag_JY_Control_Light_183_4')" class="light-glow"/>
</div>
<span class="area-label">2F_183_4</span>
</div>
<div class="light-area" @click="toggleLight('Tag_JY_Control_Light_183_1')">
<div :class="{ on: getLightStatus('Tag_JY_Control_Light_183_1') }" class="light-indicator">
<div v-if="getLightStatus('Tag_JY_Control_Light_183_1')" class="light-glow"/>
</div>
<span class="area-label">2F_183_1</span>
</div>
<div class="light-area" @click="toggleLight('Tag_JY_Control_Light_183_3')">
<div :class="{ on: getLightStatus('Tag_JY_Control_Light_183_3') }" class="light-indicator">
<div v-if="getLightStatus('Tag_JY_Control_Light_183_3')" class="light-glow"/>
</div>
<span class="area-label">2F_183_3</span>
</div>
<div class="light-area" @click="toggleLight('Tag_JY_Control_Light_183_2')">
<div :class="{ on: getLightStatus('Tag_JY_Control_Light_183_2') }" class="light-indicator">
<div v-if="getLightStatus('Tag_JY_Control_Light_183_2')" class="light-glow"/>
</div>
<span class="area-label">2F_183_2</span>
</div>
</div>
<!-- 中间右侧四分之三 -->
<div class="middle-right">
<div class="light-row">
<div
v-for="light in 4"
:key="light"
class="light-area"
@click="toggleLight('Tag_JY_Control_Light_184_' + light)">
<div :class="{ on: getLightStatus('Tag_JY_Control_Light_184_' + light) }" class="light-indicator">
<div v-if="getLightStatus('Tag_JY_Control_Light_184_' + light)" class="light-glow"/>
</div>
<span class="area-label">2F_184_{{ light }}</span>
</div>
</div>
<div class="light-row">
<div
v-for="light in 4"
:key="light"
class="light-area"
@click="toggleLight('Tag_JY_Control_Light_181_' + light)">
<div :class="{ on: getLightStatus('Tag_JY_Control_Light_181_' + light) }" class="light-indicator">
<div v-if="getLightStatus('Tag_JY_Control_Light_181_' + light)" class="light-glow"/>
</div>
<span class="area-label">2F_181_{{ light }}</span>
</div>
</div>
</div>
<div class="middle-area1">
<div class="light-area" @click="toggleLight('Tag_JY_Control_Light_180_2')">
<div :class="{ on: getLightStatus('Tag_JY_Control_Light_180_2') }" class="light-indicator">
<div v-if="getLightStatus('Tag_JY_Control_Light_180_2')" class="light-glow"/>
</div>
<span class="area-label">2F_180_2</span>
</div>
<div class="light-area" @click="toggleLight('Tag_JY_Control_Light_180_1')">
<div :class="{ on: getLightStatus('Tag_JY_Control_Light_180_1') }" class="light-indicator">
<div v-if="getLightStatus('Tag_JY_Control_Light_180_1')" class="light-glow"/>
</div>
<span class="area-label">2F_180_1</span>
</div>
</div>
<div class="middle-area2">
<div class="light-area" @click="toggleLight('Tag_JY_Control_Light_180_3')">
<div :class="{ on: getLightStatus('Tag_JY_Control_Light_180_3') }" class="light-indicator">
<div v-if="getLightStatus('Tag_JY_Control_Light_180_3')" class="light-glow"/>
</div>
<span class="area-label">2F_180_3</span>
</div>
<div class="light-area" @click="toggleLight('Tag_JY_Control_Light_180_4')">
<div :class="{ on: getLightStatus('Tag_JY_Control_Light_180_4') }" class="light-indicator">
<div v-if="getLightStatus('Tag_JY_Control_Light_180_4')" class="light-glow"/>
</div>
<span class="area-label">2F_180_4</span>
</div>
</div>
</div>
<!-- 下方区域 -->
<div class="bottom-area">
<!-- 第一行 182 -->
<div class="light-row">
<div
v-for="light in 4"
:key="'182-' + light"
class="light-area"
@click="toggleLight('Tag_JY_Control_Light_182_' + light)">
<div :class="{ on: getLightStatus('Tag_JY_Control_Light_182_' + light) }" class="light-indicator">
<div v-if="getLightStatus('Tag_JY_Control_Light_182_' + light)" class="light-glow"/>
</div>
<span class="area-label">2F_182_{{ light }}</span>
</div>
</div>
<!-- 第二行 186 -->
<div class="light-row">
<div
v-for="light in [4, 3, 2, 1]"
:key="'186-' + light"
class="light-area"
@click="toggleLight('Tag_JY_Control_Light_186_' + light)">
<div :class="{ on: getLightStatus('Tag_JY_Control_Light_186_' + light) }" class="light-indicator">
<div v-if="getLightStatus('Tag_JY_Control_Light_186_' + light)" class="light-glow"/>
</div>
<span class="area-label">2F_186_{{ light }}</span>
</div>
</div>
</div>
<div class="north-gate-label">
<div class="north-arrow"></div>
<div class="north-text">北大门</div>
</div>
</div>
</div>
</div>
<!-- 三楼 -->
<div v-show="currentFloor === 3" class="floor">
<!-- 三楼控制按钮 -->
<div class="floor-controls">
<el-button
:loading="loadingAll"
type="success"
class="control-btn"
size="large"
@click="confirmToggleAllLights(3, true)">
<i class="el-icon-open"/>
全部开启
</el-button>
<el-button
:loading="loadingAll"
type="danger"
class="control-btn"
size="large"
@click="confirmToggleAllLights(3, false)">
<i class="el-icon-turn-off"/>
全部关闭
</el-button>
<div class="floor-stats">
<span class="stat-item">开启: {{ getFloorStats(3).on }}</span>
<span class="stat-item">关闭: {{ getFloorStats(3).off }}</span>
<span class="stat-item">总数: {{ getFloorStats(3).total }}</span>
</div>
</div>
<div class="floor-content third-floor-content">
<!-- 三楼区域布局 -->
<div class="area-group third-floor-group">
<!-- 左侧三分之二区域 -->
<div class="third-left">
<!-- 第一行 -->
<div class="third-row1">
<div class="third-subrow">
<div
v-for="light in [4, 3]"
:key="light"
class="light-area"
@click="toggleLight('Tag_JY_Control_Light_191_' + light)">
<div :class="{ on: getLightStatus('Tag_JY_Control_Light_191_' + light) }" class="light-indicator">
<div v-if="getLightStatus('Tag_JY_Control_Light_191_' + light)" class="light-glow"/>
</div>
<span class="area-label">3F_191_{{ light }}</span>
</div>
</div>
<div class="third-subrow">
<div
v-for="light in [2, 1]"
:key="light"
class="light-area"
@click="toggleLight('Tag_JY_Control_Light_191_' + light)">
<div :class="{ on: getLightStatus('Tag_JY_Control_Light_191_' + light) }" class="light-indicator">
<div v-if="getLightStatus('Tag_JY_Control_Light_191_' + light)" class="light-glow"/>
</div>
<span class="area-label">3F_191_{{ light }}</span>
</div>
</div>
</div>
<!-- 第二行 -->
<div class="third-row2">
<!-- 左侧 -->
<div class="third-left-col">
<!-- 上半部分 -->
<div class="third-top">
<div class="room-label">喷漆房1</div>
<div
v-for="light in [1, 2, 3]"
:key="light"
class="light-area"
@click="toggleLight('Tag_JY_Control_Light_192_' + light)">
<div :class="{ on: getLightStatus('Tag_JY_Control_Light_192_' + light) }" class="light-indicator">
<div v-if="getLightStatus('Tag_JY_Control_Light_192_' + light)" class="light-glow"/>
</div>
<span class="area-label">3F_192_{{ light }}</span>
</div>
</div>
<!-- 下半部分 -->
<div class="third-bottom">
<div class="room-label">喷漆房2</div>
<div
v-for="light in [3, 2, 1]"
:key="light"
class="light-area"
@click="toggleLight('Tag_JY_Control_Light_193_' + light)">
<div :class="{ on: getLightStatus('Tag_JY_Control_Light_193_' + light) }" class="light-indicator">
<div v-if="getLightStatus('Tag_JY_Control_Light_193_' + light)" class="light-glow"/>
</div>
<span class="area-label">3F_193_{{ light }}</span>
</div>
</div>
</div>
<!-- 右侧 -->
<div class="third-right-col">
<div class="third-subrow">
<div
v-for="light in [4, 3]"
:key="light"
class="light-area"
@click="toggleLight('Tag_JY_Control_Light_190_' + light)">
<div :class="{ on: getLightStatus('Tag_JY_Control_Light_190_' + light) }" class="light-indicator">
<div v-if="getLightStatus('Tag_JY_Control_Light_190_' + light)" class="light-glow"/>
</div>
<span class="area-label">3F_190_{{ light }}</span>
</div>
</div>
<div class="third-subrow">
<div
v-for="light in [2, 1]"
:key="light"
class="light-area"
@click="toggleLight('Tag_JY_Control_Light_190_' + light)">
<div :class="{ on: getLightStatus('Tag_JY_Control_Light_190_' + light) }" class="light-indicator">
<div v-if="getLightStatus('Tag_JY_Control_Light_190_' + light)" class="light-glow"/>
</div>
<span class="area-label">3F_190_{{ light }}</span>
</div>
</div>
</div>
</div>
</div>
<div class="north-gate-label">
<div class="north-arrow"></div>
<div class="north-text">北大门</div>
</div>
</div>
</div>
</div>
</el-card>
</div>
</template>
<script>
import axios from 'axios'
export default {
data() {
return {
currentFloor: 1, // 默认显示一楼
loadingAll: false,
pollTimer: null, // 轮询定时器
pollInterval: 3000, // 轮询间隔5秒
// 所有灯光ID列表
allLightIds: [
// 一楼灯光
'Tag_JY_Control_Light_177_1', 'Tag_JY_Control_Light_177_2',
'Tag_JY_Control_Light_172_4', 'Tag_JY_Control_Light_172_3', 'Tag_JY_Control_Light_172_2', 'Tag_JY_Control_Light_172_1',
'Tag_JY_Control_Light_176_4', 'Tag_JY_Control_Light_176_3', 'Tag_JY_Control_Light_176_2', 'Tag_JY_Control_Light_176_1',
'Tag_JY_Control_Light_175_4', 'Tag_JY_Control_Light_175_3', 'Tag_JY_Control_Light_175_2', 'Tag_JY_Control_Light_175_1',
'Tag_JY_Control_Light_174_4', 'Tag_JY_Control_Light_174_3', 'Tag_JY_Control_Light_174_2', 'Tag_JY_Control_Light_174_1',
'Tag_JY_Control_Light_173_4', 'Tag_JY_Control_Light_173_3', 'Tag_JY_Control_Light_173_2', 'Tag_JY_Control_Light_173_1',
'Tag_JY_Control_Light_171_4', 'Tag_JY_Control_Light_171_3', 'Tag_JY_Control_Light_171_2', 'Tag_JY_Control_Light_171_1',
'Tag_JY_Control_Light_170_4', 'Tag_JY_Control_Light_170_3', 'Tag_JY_Control_Light_170_2', 'Tag_JY_Control_Light_170_1',
// 二楼灯光
'Tag_JY_Control_Light_185_1', 'Tag_JY_Control_Light_185_2', 'Tag_JY_Control_Light_185_3', 'Tag_JY_Control_Light_185_4',
'Tag_JY_Control_Light_183_4', 'Tag_JY_Control_Light_183_1', 'Tag_JY_Control_Light_183_3', 'Tag_JY_Control_Light_183_2',
'Tag_JY_Control_Light_184_1', 'Tag_JY_Control_Light_184_2', 'Tag_JY_Control_Light_184_3', 'Tag_JY_Control_Light_184_4',
'Tag_JY_Control_Light_181_1', 'Tag_JY_Control_Light_181_2', 'Tag_JY_Control_Light_181_3', 'Tag_JY_Control_Light_181_4',
'Tag_JY_Control_Light_180_2', 'Tag_JY_Control_Light_180_1', 'Tag_JY_Control_Light_180_3', 'Tag_JY_Control_Light_180_4',
'Tag_JY_Control_Light_182_1', 'Tag_JY_Control_Light_182_2', 'Tag_JY_Control_Light_182_3', 'Tag_JY_Control_Light_182_4',
'Tag_JY_Control_Light_186_4', 'Tag_JY_Control_Light_186_3', 'Tag_JY_Control_Light_186_2', 'Tag_JY_Control_Light_186_1',
// 三楼灯光
'Tag_JY_Control_Light_191_4', 'Tag_JY_Control_Light_191_3', 'Tag_JY_Control_Light_191_2', 'Tag_JY_Control_Light_191_1',
'Tag_JY_Control_Light_192_1', 'Tag_JY_Control_Light_192_2', 'Tag_JY_Control_Light_192_3',
'Tag_JY_Control_Light_193_3', 'Tag_JY_Control_Light_193_2', 'Tag_JY_Control_Light_193_1',
'Tag_JY_Control_Light_190_4', 'Tag_JY_Control_Light_190_3', 'Tag_JY_Control_Light_190_2', 'Tag_JY_Control_Light_190_1'
],
// 灯光状态数据
lightsStatus: {},
userRole: null // 用户角色编号
}
},
mounted() {
this.getUserInfo().then(() => {
this.loadAllLightsStatus()
this.startPolling()
}).catch(() => {
// 如果获取用户信息失败,仍然继续加载灯光状态
this.loadAllLightsStatus()
this.startPolling()
})
},
beforeDestroy() {
this.stopPolling()
},
methods: {
switchFloor(floor) {
this.currentFloor = floor
},
// 生成UUID
generateUUID() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
const r = Math.random() * 16 | 0
const v = c === 'x' ? r : (r & 0x3 | 0x8)
return v.toString(16)
})
},
// 构建API请求数据
buildRequestData(tagList, cmd = 'Cmd_Opc_Write') {
return {
success: false,
code: 1000,
msg: '默认值',
req_code: this.generateUUID(),
req_from: 'LightControlWeb',
req_cmd: cmd,
req_param: {
TagList: JSON.stringify(tagList)
},
res_data: null
}
},
// 读取所有灯光状态
async loadAllLightsStatus() {
try {
const tagList = this.allLightIds.map(tagId => ({
Code: -1,
Message: '',
TimeStamp: '',
TagID: tagId,
ItemName: '',
TagOrderBy: '',
OpName: '',
TagTypeCodeID: -1,
TagValue: '0'
}))
const requestData = this.buildRequestData(tagList, 'Cmd_Opc_Read')
const response = await axios.post('http://192.168.1.27:9981/api/zk/control_light', requestData)
if (response.data.success && response.data.res_data) {
const resultData = JSON.parse(response.data.res_data.StructSerialize)
// 更新灯光状态
resultData.forEach(item => {
if (item.Code === 0) {
// 使用 $set 确保响应式更新
this.$set(this.lightsStatus, item.TagID, item.TagValue === '1')
}
})
console.log('灯光状态已更新')
}
} catch (error) {
console.error('读取灯光状态失败:', error)
// 不显示错误消息,避免频繁弹窗
}
},
startPolling() {
this.stopPolling() // 先停止之前的轮询
this.pollTimer = setInterval(() => {
this.loadAllLightsStatus()
}, this.pollInterval)
},
// 停止轮询
stopPolling() {
if (this.pollTimer) {
clearInterval(this.pollTimer)
this.pollTimer = null
}
},
// 获取灯光状态
getLightStatus(lightId) {
return this.lightsStatus[lightId] || false
},
// 切换单个灯光状态
async toggleLight(lightId) {
const currentStatus = this.getLightStatus(lightId)
const newStatus = !currentStatus
this.$confirm(`确定要${newStatus ? '开启' : '关闭'} ${lightId} 吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
center: true
}).then(async() => {
try {
const tagList = [{
Code: -1,
Message: '',
TimeStamp: '',
TagID: lightId,
ItemName: '',
TagOrderBy: '',
OpName: '',
TagTypeCodeID: -1,
TagValue: newStatus ? '1' : '0'
}]
const requestData = this.buildRequestData(tagList)
const response = await axios.post('http://192.168.1.27:9981/api/zk/control_light', requestData)
if (response.data.success) {
// 只有在API调用成功后才更新状态
this.$set(this.lightsStatus, lightId, newStatus)
this.$message({
type: 'success',
message: `${lightId} ${newStatus ? '已开启' : '已关闭'}!`
})
} else {
this.$message.error('操作失败')
}
} catch (error) {
console.error('控制灯光失败:', error)
this.$message.error('控制灯光失败')
}
}).catch(() => {
this.$message({
type: 'info',
message: '已取消操作'
})
})
},
// 确认切换所有灯光状态
confirmToggleAllLights(floor, status) {
const action = status ? '开启' : '关闭'
this.$confirm(`确定要${action} ${floor}楼所有灯光吗?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
center: true
}).then(() => {
this.toggleAllLights(floor, status)
}).catch(() => {
this.$message({
type: 'info',
message: '已取消操作'
})
})
},
// 切换楼层所有灯光状态
async toggleAllLights(floor, status) {
this.loadingAll = true
try {
const prefixes = this.getFloorPrefix(floor)
const floorLights = this.allLightIds.filter(lightId =>
prefixes.some(prefix => lightId.includes(prefix))
)
// 构建TagList数组确保不为空
const tagList = floorLights.map(lightId => ({
Code: -1,
Message: '',
TimeStamp: '',
TagID: lightId,
ItemName: '',
TagOrderBy: '',
OpName: '',
TagTypeCodeID: -1,
TagValue: status ? '1' : '0'
}))
// 检查TagList是否为空
if (tagList.length === 0) {
this.$message.warning('该楼层没有可控制的灯光')
return
}
const requestData = this.buildRequestData(tagList)
const response = await axios.post('http://192.168.1.27:9981/api/zk/control_light', requestData)
if (response.data.success) {
// 只有在API调用成功后才更新所有状态
floorLights.forEach(lightId => {
this.$set(this.lightsStatus, lightId, status)
})
this.$message({
type: 'success',
message: `${floor}楼所有灯光已${status ? '开启' : '关闭'}!`
})
} else {
this.$message.error('操作失败')
}
} catch (error) {
console.error('控制所有灯光失败:', error)
this.$message.error('控制所有灯光失败')
} finally {
this.loadingAll = false
}
},
// 获取楼层前缀
getFloorPrefix(floor) {
if (floor === 1) return ['_170_', '_171_', '_172_', '_173_', '_174_', '_175_', '_176_', '_177_']
if (floor === 2) return ['_180_', '_181_', '_182_', '_183_', '_184_', '_185_', '_186_']
if (floor === 3) return ['_190_', '_191_', '_192_', '_193_']
return []
},
// 获取楼层统计信息
getFloorStats(floor) {
const prefixes = this.getFloorPrefix(floor)
const floorLights = this.allLightIds.filter(lightId =>
prefixes.some(prefix => lightId.includes(prefix))
)
const on = floorLights.filter(lightId => this.lightsStatus[lightId]).length
const off = floorLights.filter(lightId => !this.lightsStatus[lightId]).length
const total = floorLights.length
return { on, off, total }
},
// 获取用户信息
async getUserInfo() {
try {
// 假设token存储在localStorage或vuex中
const token = localStorage.getItem('token') || this.$store.state.token
if (!token) {
console.warn('未找到用户token')
return
}
const response = await this.$api.getInfo(token)
if (response && response.data && response.data.length > 0) {
this.userRole = response.data[0].角色编号
console.log('获取用户角色成功:', this.userRole)
}
} catch (error) {
console.error('获取用户信息失败:', error)
}
},
// 修改构建API请求数据方法包含用户角色信息
buildRequestData(tagList, cmd = 'Cmd_Opc_Write') {
// 构建req_from字段包含角色编号
let reqFrom = 'LightControlWeb'
if (this.userRole) {
reqFrom = `LightControlWeb_Role${this.userRole}`
}
return {
success: false,
code: 1000,
msg: '默认值',
req_code: this.generateUUID(),
req_from: reqFrom, // 包含角色编号
req_cmd: cmd,
req_param: {
TagList: JSON.stringify(tagList)
},
res_data: null
}
}
}
}
</script>
<style scoped>
/* 原有的所有CSS样式保持不变 */
.main-card {
border: none !important;
border-radius: 12px !important;
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.3) !important;
position: relative;
}
/* 楼层切换按钮 */
.floor-switcher {
position: absolute;
top: 20px;
left: 20px;
z-index: 10;
display: flex;
gap: 15px;
}
.floor-btn {
background: linear-gradient(135deg, #3498db, #2980b9) !important;
border: none !important;
color: white !important;
font-weight: bold;
font-size: 16px;
padding: 15px 25px !important;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
transition: all 0.3s ease;
border-radius: 8px !important;
min-height: 50px;
display: flex;
align-items: center;
justify-content: center;
}
.floor-btn:hover {
transform: translateY(-3px);
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.4);
}
.floor-btn:active {
transform: translateY(1px);
}
.floor-btn:focus {
background: linear-gradient(135deg, #2ecc71, #27ae60) !important;
}
/* 调整楼层内容的上边距,为按钮留出空间 */
.floor {
width: 100%;
height: 740px;
margin-bottom: 20px;
position: relative;
background: linear-gradient(145deg, #34495e, #2c3e50);
border-radius: 10px;
padding: 15px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
margin-top: 100px;
/* 为按钮留出空间 */
}
.floor-name {
text-align: center;
height: 30px;
width: 100%;
font-size: 1.2rem;
font-weight: bold;
color: #3498db;
text-shadow: 0 2px 5px rgba(0, 0, 0, 0.5);
margin-bottom: 10px;
}
.floor-content {
height: 100%;
display: flex;
justify-content: space-between;
align-items: center;
}
/* 二楼专用样式 */
.second-floor-content {
padding: 0;
}
.second-floor-group {
width: 100%;
height: 100%;
flex-direction: column;
justify-content: space-around;
border-radius: 8px;
padding: 15px;
}
/* 二楼上方区域 */
.top-area {
display: flex;
justify-content: space-around;
width: 86%;
height: 50px;
align-items: center;
border-radius: 6px;
/* 添加圆角 */
padding: 10px;
/* 添加内边距 */
}
/* 二楼中间区域 */
.middle-area {
display: flex;
width: 100%;
height: 215px;
gap: 15px;
position: relative;
}
.middle-area1 {
width: 250px;
/* 增加宽度以匹配长文字 */
height: 380px;
position: absolute;
top: -150px;
right: 550px;
/* 使用 right 而不是 left */
display: flex;
flex-direction: column;
justify-content: space-around;
border-radius: 6px;
padding: 10px;
z-index: 10;
}
.middle-area2 {
width: 600px;
height: 74px;
position: absolute;
bottom: -100px;
/* 使用 bottom 而不是 top */
right: 740px;
display: flex;
flex-direction: row;
justify-content: space-around;
border-radius: 6px;
padding: 10px;
}
.middle-left {
width: 25%;
display: grid;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
gap: 8px;
border-radius: 6px;
padding: 10px;
background: rgba(255, 255, 255, 0.05);
/* 添加背景阴影 */
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
/* 添加阴影效果 */
}
.middle-left .light-area {
display: flex;
align-items: center;
justify-content: center;
}
.middle-left .light-area:nth-child(1) {
grid-area: 1 / 2 / 2 / 3;
}
/* 4 */
.middle-left .light-area:nth-child(2) {
grid-area: 1 / 1 / 2 / 2;
}
/* 1 */
.middle-left .light-area:nth-child(3) {
grid-area: 2 / 1 / 3 / 2;
}
/* 3 */
.middle-left .light-area:nth-child(4) {
grid-area: 2 / 2 / 3 / 3;
}
/* 2 */
.middle-right {
width: 75%;
display: flex;
flex-direction: column;
justify-content: space-between;
border-radius: 6px;
padding: 10px;
}
.light-row {
display: flex;
justify-content: space-around;
width: 100%;
}
/* 二楼下方区域 */
.bottom-area {
display: flex;
flex-direction: column;
justify-content: space-around;
width: 66.67%;
/* 三分之二 */
height: 120px;
/* 增加高度以适应两行 */
align-items: center;
border-radius: 6px;
padding: 10px 20px;
gap: 35px;
}
.bottom-area .light-row {
display: flex;
justify-content: space-around;
width: 100%;
}
.area-group {
display: flex;
gap: 80px;
height: 100%;
}
/* 三楼样式 - 横向排列 */
.third-floor-group {
width: 100%;
flex-direction: column;
justify-content: space-between;
}
.area-row {
display: flex;
justify-content: space-around;
width: 100%;
gap: 10px;
}
/* 一楼左侧区域 */
.left-group {
flex-direction: column;
width: 250px;
height: 100%;
justify-content: flex-start;
gap: 20px;
}
.group1,
.group2 {
border-radius: 8px;
padding: 10px;
display: flex;
flex-direction: column;
gap: 12px;
}
.group1 {
height: 130px;
}
.group2 {
height: 160px;
gap: 12px;
flex-grow: 1;
}
/* 一楼中间区域 */
.center-group {
flex: 1;
max-width: 1550px;
flex-direction: column;
justify-content: center;
border-radius: 8px;
gap: 70px;
}
/* 一楼右侧区域 */
.right-group {
width: 250px;
height: 100%;
}
.right-group .area-column {
border-radius: 8px;
padding: 15px;
height: 100%;
display: flex;
/* 添加此行 */
flex-direction: column;
/* 添加此行 */
justify-content: space-between;
/* 添加此行 */
width: 100%;
}
.light-area {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 8px 5px;
border-radius: 5px;
transition: all 0.3s ease;
width: 100%;
flex: 1;
/* 添加此行,使每个区域均分高度 */
min-height: 0;
/* 防止内容溢出 */
cursor: pointer;
position: relative;
overflow: hidden;
margin: 2px 0;
}
.light-area:hover {
transform: translateY(-2px);
background: rgba(255, 255, 255, 0.1);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
.light-area:active {
transform: translateY(1px);
}
.light-indicator {
width: 16px;
height: 16px;
border-radius: 50%;
background: linear-gradient(135deg, #e74c3c, #c0392b);
box-shadow: 0 0 8px rgba(231, 76, 60, 0.5);
transition: all 0.3s ease;
flex-shrink: 0;
position: relative;
z-index: 2;
}
.light-indicator.on {
background: linear-gradient(135deg, #2ecc71, #27ae60);
box-shadow: 0 0 12px rgba(46, 204, 113, 0.7);
animation: pulse 2s infinite;
}
.light-glow {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 24px;
height: 24px;
border-radius: 50%;
background: rgba(46, 204, 113, 0.4);
filter: blur(5px);
z-index: 1;
animation: glow-pulse 2s infinite;
}
@keyframes pulse {
0% {
box-shadow: 0 0 0 0 rgba(46, 204, 113, 0.7);
}
70% {
box-shadow: 0 0 0 10px rgba(46, 204, 113, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(46, 204, 113, 0);
}
}
@keyframes glow-pulse {
0%,
100% {
opacity: 0.6;
transform: translate(-50%, -50%) scale(1);
}
50% {
opacity: 0.8;
transform: translate(-50%, -50%) scale(1.2);
}
}
.area-label {
color: #ecf0f1;
font-size: 0.8rem;
font-weight: 500;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
flex-shrink: 0;
}
/* 响应式设计 */
@media (max-width: 1200px) {
.center-group {
max-width: 600px;
margin: 0 20px;
}
}
@media (max-width: 768px) {
.floor-content {
flex-direction: column;
overflow-y: auto;
}
.left-group,
.center-group,
.right-group {
width: 100%;
margin: 10px 0;
}
.floor-switcher {
position: relative;
top: 0;
left: 0;
justify-content: center;
margin-bottom: 20px;
}
}
/* 三楼专用样式 */
.third-floor-content {
padding: 0;
width: 100%;
height: 100%;
}
.third-floor-group {
width: 100%;
height: 100%;
display: flex;
gap: 15px;
}
/* 左侧三分之二区域 */
.third-left {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
gap: 15px;
border-radius: 8px;
padding: 15px;
}
/* 第一行 - 占50%高度 */
.third-row1 {
height: 28%;
display: flex;
flex-direction: column;
gap: 10px;
}
.third-subrow {
flex: 1;
display: flex;
gap: 10px;
}
.third-subrow .light-area {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
}
/* 第二行 - 占50%高度 */
.third-row2 {
height: 70%;
display: flex;
gap: 15px;
}
.third-left-col {
flex: 1;
display: flex;
flex-direction: column;
gap: 10px;
}
.third-top,
.third-bottom {
flex: 1;
display: flex;
flex-direction: column;
gap: 5px;
background: rgba(255, 255, 255, 0.05);
border-radius: 6px;
padding: 8px;
position: relative;
}
.room-label {
position: absolute;
top: 5px;
left: 5px;
color: #ecf0f1;
font-size: 0.7rem;
font-weight: bold;
background: rgba(0, 0, 0, 0.5);
padding: 2px 6px;
border-radius: 3px;
z-index: 5;
}
.third-top .light-area,
.third-bottom .light-area {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
margin-top: 15px;
}
.third-right-col {
flex: 1;
display: flex;
flex-direction: column;
gap: 10px;
}
.third-right-col .third-subrow {
flex: 1;
display: flex;
gap: 10px;
}
.third-right-col .light-area {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
}
/* 右侧三分之一区域 */
/* 响应式设计 */
@media (max-width: 768px) {
.third-floor-group {
flex-direction: column;
}
.third-left,
.third-right {
width: 100%;
height: 50%;
}
}
.floor-controls {
position: absolute;
top: -80px;
right: 20px;
display: flex;
align-items: center;
gap: 20px;
z-index: 10;
}
.control-btn {
font-weight: bold;
font-size: 16px;
padding: 15px 25px !important;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
transition: all 0.3s ease;
border-radius: 8px !important;
min-height: 50px;
display: flex;
align-items: center;
justify-content: center;
}
.control-btn:hover {
transform: translateY(-3px);
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.4);
}
.control-btn:active {
transform: translateY(1px);
}
.floor-stats {
display: flex;
gap: 20px;
padding: 15px 25px;
border-radius: 20px;
backdrop-filter: blur(10px);
background: rgba(68, 68, 68, 0.1);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
min-height: 50px;
align-items: center;
}
.stat-item {
color: #383838;
font-weight: bold;
font-size: 1rem;
}
.north-gate-label {
position: absolute;
right: 23%;
/* 从右数三分之一处 */
bottom: 20px;
display: flex;
flex-direction: column;
align-items: center;
z-index: 5;
background: rgba(52, 152, 219, 0.8);
padding: 8px 12px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}
.north-arrow {
font-size: 24px;
color: white;
font-weight: bold;
margin-bottom: 2px;
}
.north-text {
color: white;
font-size: 14px;
font-weight: bold;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
}
</style>