1410 lines
42 KiB
Vue
1410 lines
42 KiB
Vue
<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>
|