|
|
@@ -0,0 +1,700 @@
|
|
|
+<template>
|
|
|
+ <div class="room-management-container"> <!-- 修改为 room-management-container -->
|
|
|
+ <h1>房间管理</h1>
|
|
|
+ <!-- 提示信息 -->
|
|
|
+ <div v-if="message" :class="['message', messageType]">
|
|
|
+ {{ message }}
|
|
|
+ </div>
|
|
|
+ <button class="add-room-button" @click="showModal = true">新增房间</button>
|
|
|
+
|
|
|
+ <!-- 新增房间的模态框 -->
|
|
|
+ <div v-if="showModal" class="modal">
|
|
|
+ <div class="modal-content">
|
|
|
+ <span class="close" @click="showModal = false">×</span>
|
|
|
+ <h2>新增房间</h2>
|
|
|
+ <input v-model="newRoomName" placeholder="请输入房间名称" />
|
|
|
+ <input v-model="newRoomDescription" placeholder="请输入房间描述" />
|
|
|
+ <input v-model="newRoomFloor" placeholder="请输入楼层" type="number" />
|
|
|
+ <select v-model="newRoomOrientation">
|
|
|
+ <option value="" disabled>请选择朝向</option>
|
|
|
+ <option value="东">东</option>
|
|
|
+ <option value="南">南</option>
|
|
|
+ <option value="西">西</option>
|
|
|
+ <option value="北">北</option>
|
|
|
+ </select>
|
|
|
+ <button class="modal-button" @click="addNewRoom">确定</button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 显示所有房间的列表 -->
|
|
|
+ <h2>所有房间</h2>
|
|
|
+ <input v-model="roomFilter" placeholder="搜索房间名称" class="filter-input" />
|
|
|
+ <table class="room-table">
|
|
|
+ <thead>
|
|
|
+ <tr>
|
|
|
+ <th>房间名称</th>
|
|
|
+ <th>描述</th>
|
|
|
+ <th>楼层</th>
|
|
|
+ <th>朝向</th>
|
|
|
+ <th>操作</th>
|
|
|
+ </tr>
|
|
|
+ </thead>
|
|
|
+ <tbody>
|
|
|
+ <tr v-for="room in paginatedRooms" :key="room.id">
|
|
|
+ <td>{{ room.room_name }}</td>
|
|
|
+ <td>{{ room.description }}</td>
|
|
|
+ <td>{{ room.floor }}</td>
|
|
|
+ <td>{{ room.orientation }}</td>
|
|
|
+ <td>
|
|
|
+ <!-- 只有绑定设备的房间才显示"查看设备"按钮 -->
|
|
|
+ <button
|
|
|
+ v-if="room.device_count > 0"
|
|
|
+ class="view-devices-button"
|
|
|
+ @click="toggleDevices(room.id)"
|
|
|
+ >
|
|
|
+ 查看设备
|
|
|
+ </button>
|
|
|
+ <button class="edit-button" @click="openEditModal(room)">编辑</button>
|
|
|
+ </td>
|
|
|
+ </tr>
|
|
|
+ </tbody>
|
|
|
+ </table>
|
|
|
+ <div class="pagination">
|
|
|
+ <button @click="prevRoomPage" :disabled="currentRoomPage === 1">上一页</button>
|
|
|
+ <span>第 {{ currentRoomPage }} 页</span>
|
|
|
+ <button @click="nextRoomPage" :disabled="currentRoomPage === totalRoomPages">下一页</button>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 编辑房间的模态框 -->
|
|
|
+<div v-if="showEditModal" class="modal">
|
|
|
+ <div class="modal-content">
|
|
|
+ <span class="close" @click="showEditModal = false">×</span>
|
|
|
+ <h2>编辑房间</h2>
|
|
|
+ <input v-model="editRoom.room_name" placeholder="请输入房间名称" />
|
|
|
+ <input v-model="editRoom.description" placeholder="请输入房间描述" />
|
|
|
+ <input v-model="editRoom.floor" placeholder="请输入楼层" type="number" />
|
|
|
+ <select v-model="editRoom.orientation">
|
|
|
+ <option value="" disabled>请选择朝向</option>
|
|
|
+ <option value="东">东</option>
|
|
|
+ <option value="南">南</option>
|
|
|
+ <option value="西">西</option>
|
|
|
+ <option value="北">北</option>
|
|
|
+ </select>
|
|
|
+ <button class="modal-button" @click="saveEditedRoom">保存</button>
|
|
|
+ </div>
|
|
|
+</div>
|
|
|
+
|
|
|
+<!-- 查看设备弹框 -->
|
|
|
+<div v-if="showDevicesModal" class="device-modal">
|
|
|
+ <div class="device-modal-content">
|
|
|
+ <span class="close" @click="showDevicesModal = false">×</span>
|
|
|
+ <h2>已绑定设备</h2>
|
|
|
+ <table class="device-table">
|
|
|
+ <thead>
|
|
|
+ <tr>
|
|
|
+ <th>设备ID</th>
|
|
|
+ <th>设备名称</th>
|
|
|
+ <th>在线状态</th>
|
|
|
+ <th>温度</th>
|
|
|
+ <th>继电器状态</th>
|
|
|
+ <th>人体传感器状态</th>
|
|
|
+ <th>操作</th>
|
|
|
+ </tr>
|
|
|
+ </thead>
|
|
|
+ <tbody>
|
|
|
+ <tr v-for="device in boundDevices" :key="device.device_id">
|
|
|
+ <td>{{ device.device_id }}</td>
|
|
|
+ <td>{{ device.name }}</td>
|
|
|
+ <td>
|
|
|
+ <span :style="{ color: device.status === 'online' ? 'green' : 'red' }">
|
|
|
+ {{ device.status === 'online' ? '在线' : '离线' }}
|
|
|
+ </span>
|
|
|
+ </td>
|
|
|
+ <td>{{ device.temperature }}°C</td>
|
|
|
+ <td>
|
|
|
+ <span :style="{ color: device.switch_status === 'on' ? 'green' : 'red' }">
|
|
|
+ {{ device.switch_status === 'on' ? '开启' : '关闭' }}
|
|
|
+ </span>
|
|
|
+ </td>
|
|
|
+ <td>
|
|
|
+ <span :style="{ color: device.level_status === 'high' ? 'green' : 'red' }">
|
|
|
+ {{ device.level_status === 'high' ? '有人' : '无人' }}
|
|
|
+ </span>
|
|
|
+ </td>
|
|
|
+ <td>
|
|
|
+ <!-- 绑定继电器按钮 -->
|
|
|
+ <button
|
|
|
+ v-if="device.device_id.includes('24G-') && hasRelayInRoom"
|
|
|
+ class="bind-button"
|
|
|
+ @click="bindRelayToSensor(device.device_id)"
|
|
|
+ >
|
|
|
+ 绑定继电器
|
|
|
+ </button>
|
|
|
+ </td>
|
|
|
+ </tr>
|
|
|
+ </tbody>
|
|
|
+ </table>
|
|
|
+ </div>
|
|
|
+</div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+export default {
|
|
|
+ name: 'RoomManagement',
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ message: '',
|
|
|
+ messageType: '',
|
|
|
+ rooms: [],
|
|
|
+ roomFilter: '',
|
|
|
+ showModal: false,
|
|
|
+ newRoomName: '',
|
|
|
+ newRoomDescription: '',
|
|
|
+ newRoomFloor: null,
|
|
|
+ newRoomOrientation: '',
|
|
|
+ currentRoomPage: 1,
|
|
|
+ itemsPerPage: 10,
|
|
|
+ showDevicesModal: false,
|
|
|
+ boundDevices: [],
|
|
|
+ showEditModal: false,
|
|
|
+ editRoom: {
|
|
|
+ id: null,
|
|
|
+ room_name: '',
|
|
|
+ description: '',
|
|
|
+ floor: null,
|
|
|
+ orientation: '',
|
|
|
+ },
|
|
|
+ hasHumanSensor: false, // 是否有人体传感器
|
|
|
+ hasRelayInRoom: false, // 是否有继电器
|
|
|
+ roomId: null, // 当前查看的房间ID
|
|
|
+ };
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ filteredRooms() {
|
|
|
+ return this.rooms.filter(room =>
|
|
|
+ room.room_name.includes(this.roomFilter)
|
|
|
+ );
|
|
|
+ },
|
|
|
+ paginatedRooms() {
|
|
|
+ const start = (this.currentRoomPage - 1) * this.itemsPerPage;
|
|
|
+ const end = start + this.itemsPerPage;
|
|
|
+ return this.filteredRooms.slice(start, end);
|
|
|
+ },
|
|
|
+ totalRoomPages() {
|
|
|
+ return Math.ceil(this.filteredRooms.length / this.itemsPerPage);
|
|
|
+ },
|
|
|
+ },
|
|
|
+ async created() {
|
|
|
+ await this.fetchRooms();
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ // 打开编辑房间的模态框
|
|
|
+ openEditModal(room) {
|
|
|
+ this.editRoom = { ...room }; // 将当前房间信息赋值给 editRoom
|
|
|
+ this.showEditModal = true; // 显示编辑模态框
|
|
|
+ },
|
|
|
+ prevRoomPage() {
|
|
|
+ if (this.currentRoomPage > 1) this.currentRoomPage--;
|
|
|
+ },
|
|
|
+ nextRoomPage() {
|
|
|
+ if (this.currentRoomPage < this.totalRoomPages) this.currentRoomPage++;
|
|
|
+ },
|
|
|
+ async fetchRooms() {
|
|
|
+ try {
|
|
|
+ // 获取房间列表
|
|
|
+ const response = await fetch('/api/rooms');
|
|
|
+ if (!response.ok) {
|
|
|
+ console.error('Failed to fetch rooms:', response.statusText);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const rooms = await response.json();
|
|
|
+
|
|
|
+ // 为每个房间获取绑定的设备数量
|
|
|
+ const roomsWithDeviceCount = await Promise.all(
|
|
|
+ rooms.map(async (room) => {
|
|
|
+ const devicesResponse = await fetch(`/api/devices-by-room?roomId=${room.id}`);
|
|
|
+ if (!devicesResponse.ok) {
|
|
|
+ console.error('Failed to fetch devices for room:', room.id);
|
|
|
+ return { ...room, device_count: 0 };
|
|
|
+ }
|
|
|
+ const devices = await devicesResponse.json();
|
|
|
+ return { ...room, device_count: devices.length };
|
|
|
+ })
|
|
|
+ );
|
|
|
+
|
|
|
+ this.rooms = roomsWithDeviceCount;
|
|
|
+ } catch (error) {
|
|
|
+ console.error('Error fetching rooms:', error);
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 绑定继电器到人体传感器
|
|
|
+ async bindRelayToSensor(sensorId) {
|
|
|
+ try {
|
|
|
+ const response = await fetch('/api/devices/bind-relay-to-sensor', {
|
|
|
+ method: 'POST',
|
|
|
+ headers: {
|
|
|
+ 'Content-Type': 'application/json',
|
|
|
+ },
|
|
|
+ body: JSON.stringify({
|
|
|
+ sensorId: sensorId,
|
|
|
+ roomId: this.roomId,
|
|
|
+ }),
|
|
|
+ });
|
|
|
+
|
|
|
+ if (response.ok) {
|
|
|
+ const result = await response.json();
|
|
|
+ this.message = result.message || '继电器绑定成功';
|
|
|
+ this.messageType = 'success';
|
|
|
+ console.log('bindRelayToSensor 返回结果:', result);
|
|
|
+ // 重新获取设备列表以更新界面
|
|
|
+ await this.toggleDevices(this.roomId);
|
|
|
+ } else {
|
|
|
+ this.message = '绑定继电器时出错';
|
|
|
+ this.messageType = 'error';
|
|
|
+ console.error('绑定继电器时出错');
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ this.message = '绑定设备时出错';
|
|
|
+ this.messageType = 'error';
|
|
|
+ console.error('绑定设备时出错:', error);
|
|
|
+ }
|
|
|
+},
|
|
|
+
|
|
|
+async toggleDevices(roomId) {
|
|
|
+ this.roomId = roomId; // 保存当前房间ID
|
|
|
+ try {
|
|
|
+ const response = await fetch('/api/devices-by-room?roomId=' + roomId);
|
|
|
+ if (response.ok) {
|
|
|
+ this.boundDevices = await response.json();
|
|
|
+
|
|
|
+ // 检查房间中是否同时存在人体传感器和继电器
|
|
|
+ this.hasHumanSensor = this.boundDevices.some(
|
|
|
+ (device) => device.device_id.includes('24G-')
|
|
|
+ );
|
|
|
+ this.hasRelayInRoom = this.boundDevices.some(
|
|
|
+ (device) => device.device_id.includes('ESP32-')
|
|
|
+ );
|
|
|
+
|
|
|
+ this.showDevicesModal = true;
|
|
|
+ } else {
|
|
|
+ console.error('Failed to fetch devices:', response.statusText);
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('Error fetching devices:', error);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ },
|
|
|
+};
|
|
|
+</script>
|
|
|
+
|
|
|
+<style>/* 房间管理界面的整体布局 */
|
|
|
+.room-management-container {
|
|
|
+ max-height: 90vh; /* 设置最大高度为视口的 90% */
|
|
|
+ overflow-y: auto; /* 启用垂直滚动条 */
|
|
|
+ padding: 20px; /* 内边距 */
|
|
|
+ background-color: #f5f7fa; /* 背景颜色 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 主标题样式 */
|
|
|
+.page-title {
|
|
|
+ font-size: 24px; /* 字体大小 */
|
|
|
+ margin-bottom: 20px; /* 底部外边距 */
|
|
|
+ color: #333; /* 字体颜色 */
|
|
|
+ text-align: center; /* 文字居中 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 楼层标题样式 */
|
|
|
+.floor-title {
|
|
|
+ font-size: 20px; /* 字体大小 */
|
|
|
+ margin-bottom: 15px; /* 底部外边距 */
|
|
|
+ color: #555; /* 字体颜色 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 楼层内容区域样式 */
|
|
|
+.floor-content {
|
|
|
+ display: flex; /* 弹性布局 */
|
|
|
+ gap: 20px; /* 子元素间距 */
|
|
|
+ margin-bottom: 30px; /* 底部外边距 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 房间列表样式 */
|
|
|
+.room-list {
|
|
|
+ flex: 1; /* 占据剩余空间 */
|
|
|
+ display: flex; /* 弹性布局 */
|
|
|
+ flex-wrap: wrap; /* 允许换行 */
|
|
|
+ gap: 10px; /* 子元素间距 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 房间卡片样式 */
|
|
|
+.room-card {
|
|
|
+ flex: 1 1 calc(25% - 10px); /* 每行显示 4 个房间卡片 */
|
|
|
+ border: 1px solid #e0e0e0; /* 边框 */
|
|
|
+ border-radius: 8px; /* 圆角 */
|
|
|
+ padding: 15px; /* 内边距 */
|
|
|
+ background-color: #fff; /* 背景颜色 */
|
|
|
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); /* 阴影效果 */
|
|
|
+ transition: transform 0.2s, box-shadow 0.2s; /* 过渡效果 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 房间卡片悬停时的样式 */
|
|
|
+.room-card:hover {
|
|
|
+ transform: translateY(-3px); /* 上移效果 */
|
|
|
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); /* 阴影效果 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 房间卡片标题样式 */
|
|
|
+.room-card h4 {
|
|
|
+ font-size: 18px; /* 字体大小 */
|
|
|
+ margin: 0 0 10px; /* 底部外边距 */
|
|
|
+ color: #333; /* 字体颜色 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 房间卡片内容样式 */
|
|
|
+.room-card p {
|
|
|
+ font-size: 14px; /* 字体大小 */
|
|
|
+ margin: 6px 0; /* 上下外边距 */
|
|
|
+ color: #666; /* 字体颜色 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 房间状态样式 */
|
|
|
+.room-card p span {
|
|
|
+ font-weight: 500; /* 字体粗细 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 房间状态为"有人"时的样式 */
|
|
|
+.room-card.occupied {
|
|
|
+ background-color: #fff3cd; /* 背景颜色 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 房间状态为"无人"时的样式 */
|
|
|
+.room-card.unoccupied {
|
|
|
+ background-color: #d4edda; /* 背景颜色 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 房间状态为"人体存在掉线"时的样式 */
|
|
|
+.room-card.sensor-offline {
|
|
|
+ background-color: #f8d7da; /* 背景颜色 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 房间状态为"离线"时的样式 */
|
|
|
+.room-card.offline {
|
|
|
+ background-color: #f8f8f8; /* 背景颜色 */
|
|
|
+ color: #999; /* 字体颜色 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 新增房间按钮样式 */
|
|
|
+.add-room-button {
|
|
|
+ background: linear-gradient(135deg, #4caf50, #81c784); /* 渐变背景 */
|
|
|
+ color: white; /* 字体颜色 */
|
|
|
+ border: none; /* 去除边框 */
|
|
|
+ padding: 10px 20px; /* 内边距 */
|
|
|
+ border-radius: 6px; /* 圆角 */
|
|
|
+ font-size: 16px; /* 字体大小 */
|
|
|
+ font-weight: 500; /* 字体粗细 */
|
|
|
+ cursor: pointer; /* 鼠标指针 */
|
|
|
+ transition: all 0.3s ease; /* 过渡效果 */
|
|
|
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); /* 阴影效果 */
|
|
|
+ margin-bottom: 20px; /* 底部外边距 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 新增房间按钮悬停时的样式 */
|
|
|
+.add-room-button:hover {
|
|
|
+ background: linear-gradient(135deg, #45a049, #6bbf70); /* 渐变背景 */
|
|
|
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); /* 阴影效果 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 模态框按钮样式 */
|
|
|
+.modal-button {
|
|
|
+ background: linear-gradient(135deg, #4caf50, #81c784); /* 渐变背景 */
|
|
|
+ color: white; /* 字体颜色 */
|
|
|
+ border: none; /* 去除边框 */
|
|
|
+ padding: 10px 20px; /* 内边距 */
|
|
|
+ border-radius: 6px; /* 圆角 */
|
|
|
+ font-size: 16px; /* 字体大小 */
|
|
|
+ font-weight: 500; /* 字体粗细 */
|
|
|
+ cursor: pointer; /* 鼠标指针 */
|
|
|
+ transition: all 0.3s ease; /* 过渡效果 */
|
|
|
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); /* 阴影效果 */
|
|
|
+ margin-top: 10px; /* 顶部外边距 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 模态框按钮悬停时的样式 */
|
|
|
+.modal-button:hover {
|
|
|
+ background: linear-gradient(135deg, #45a049, #6bbf70); /* 渐变背景 */
|
|
|
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); /* 阴影效果 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 输入框和选择框的样式 */
|
|
|
+.modal-content input,
|
|
|
+.modal-content select {
|
|
|
+ width: 100%; /* 宽度 */
|
|
|
+ padding: 8px; /* 内边距 */
|
|
|
+ border: 1px solid #e0e0e0; /* 边框 */
|
|
|
+ border-radius: 6px; /* 圆角 */
|
|
|
+ font-size: 14px; /* 字体大小 */
|
|
|
+ transition: all 0.3s ease; /* 过渡效果 */
|
|
|
+ margin-bottom: 15px; /* 底部外边距 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 输入框和选择框聚焦时的样式 */
|
|
|
+.modal-content input:focus,
|
|
|
+.modal-content select:focus {
|
|
|
+ border-color: #4caf50; /* 边框颜色 */
|
|
|
+ box-shadow: 0 0 8px rgba(76, 175, 80, 0.3); /* 阴影效果 */
|
|
|
+ outline: none; /* 去除默认的聚焦轮廓 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 输入框和选择框悬停时的样式 */
|
|
|
+.modal-content input:hover,
|
|
|
+.modal-content select:hover {
|
|
|
+ border-color: #4caf50; /* 边框颜色 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 模态框样式 */
|
|
|
+.modal {
|
|
|
+ position: fixed; /* 固定定位 */
|
|
|
+ top: 0; /* 顶部距离 */
|
|
|
+ left: 0; /* 左侧距离 */
|
|
|
+ width: 100%; /* 宽度 */
|
|
|
+ height: 100%; /* 高度 */
|
|
|
+ background-color: rgba(0, 0, 0, 0.5); /* 背景颜色 */
|
|
|
+ display: flex; /* 弹性布局 */
|
|
|
+ justify-content: center; /* 水平居中 */
|
|
|
+ align-items: center; /* 垂直居中 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 模态框内容样式 */
|
|
|
+.modal-content {
|
|
|
+ background-color: white; /* 背景颜色 */
|
|
|
+ padding: 20px; /* 内边距 */
|
|
|
+ border-radius: 8px; /* 圆角 */
|
|
|
+ width: 400px; /* 宽度 */
|
|
|
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); /* 阴影效果 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 模态框标题样式 */
|
|
|
+.modal-content h2 {
|
|
|
+ margin-top: 0; /* 顶部外边距 */
|
|
|
+ color: #333; /* 字体颜色 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 关闭按钮样式 */
|
|
|
+.close {
|
|
|
+ float: right; /* 右浮动 */
|
|
|
+ font-size: 24px; /* 字体大小 */
|
|
|
+ cursor: pointer; /* 鼠标指针 */
|
|
|
+ color: #666; /* 字体颜色 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 关闭按钮悬停时的样式 */
|
|
|
+.close:hover {
|
|
|
+ color: #333; /* 字体颜色 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 分页按钮的样式 */
|
|
|
+.pagination {
|
|
|
+ margin-top: 20px; /* 顶部外边距 */
|
|
|
+ text-align: center; /* 文字居中 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 分页按钮的样式 */
|
|
|
+.pagination button {
|
|
|
+ padding: 8px 16px; /* 内边距 */
|
|
|
+ border: none; /* 去除边框 */
|
|
|
+ border-radius: 6px; /* 圆角 */
|
|
|
+ font-size: 14px; /* 字体大小 */
|
|
|
+ font-weight: 500; /* 字体粗细 */
|
|
|
+ cursor: pointer; /* 鼠标指针 */
|
|
|
+ transition: all 0.3s ease; /* 过渡效果 */
|
|
|
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); /* 阴影效果 */
|
|
|
+ background-color: #4caf50; /* 背景颜色 */
|
|
|
+ color: white; /* 字体颜色 */
|
|
|
+ margin: 0 5px; /* 左右外边距 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 分页按钮悬停时的样式 */
|
|
|
+.pagination button:hover {
|
|
|
+ background-color: #45a049; /* 背景颜色 */
|
|
|
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); /* 阴影效果 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 分页按钮禁用时的样式 */
|
|
|
+.pagination button:disabled {
|
|
|
+ background-color: #e0e0e0; /* 背景颜色 */
|
|
|
+ color: #999; /* 字体颜色 */
|
|
|
+ cursor: not-allowed; /* 禁用鼠标指针 */
|
|
|
+ box-shadow: none; /* 去除阴影 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 表格样式 */
|
|
|
+.room-table {
|
|
|
+ width: 100%; /* 表格宽度 */
|
|
|
+ border-collapse: separate; /* 使用 separate 而不是 collapse */
|
|
|
+ border-spacing: 0; /* 单元格间距 */
|
|
|
+ margin-bottom: 20px; /* 底部外边距 */
|
|
|
+ background-color: #fff; /* 背景颜色 */
|
|
|
+ border-radius: 8px; /* 圆角 */
|
|
|
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); /* 阴影效果 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 表头样式 */
|
|
|
+.room-table th {
|
|
|
+ background-color: #f8f9fa; /* 表头背景颜色 */
|
|
|
+ font-weight: 600; /* 字体粗细 */
|
|
|
+ color: #333; /* 字体颜色 */
|
|
|
+ padding: 12px; /* 内边距 */
|
|
|
+ text-align: center; /* 文字居中 */
|
|
|
+ border-bottom: 2px solid #e0e0e0; /* 底部边框 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 表格数据样式 */
|
|
|
+.room-table td {
|
|
|
+ padding: 12px; /* 内边距 */
|
|
|
+ text-align: center; /* 文字居中 */
|
|
|
+ border-bottom: 1px solid #e0e0e0; /* 底部边框 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 表格行悬停效果 */
|
|
|
+.room-table tr:hover {
|
|
|
+ background-color: #f1f1f1; /* 悬停背景颜色 */
|
|
|
+ transition: background-color 0.3s ease; /* 过渡效果 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 表格最后一行去除底部边框 */
|
|
|
+.room-table tr:last-child td {
|
|
|
+ border-bottom: none; /* 去除底部边框 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 查看设备弹框样式 */
|
|
|
+.device-modal {
|
|
|
+ position: fixed;
|
|
|
+ top: 0;
|
|
|
+ left: 0;
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ background-color: rgba(0, 0, 0, 0.5);
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+.device-modal-content {
|
|
|
+ background-color: white;
|
|
|
+ padding: 20px;
|
|
|
+ border-radius: 8px;
|
|
|
+ width: 80%;
|
|
|
+ max-width: 800px;
|
|
|
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
|
|
+}
|
|
|
+
|
|
|
+.device-modal-content h2 {
|
|
|
+ margin-top: 0;
|
|
|
+ color: #333;
|
|
|
+}
|
|
|
+
|
|
|
+.device-modal-content .close {
|
|
|
+ float: right;
|
|
|
+ font-size: 24px;
|
|
|
+ cursor: pointer;
|
|
|
+ color: #666;
|
|
|
+}
|
|
|
+
|
|
|
+.device-modal-content .close:hover {
|
|
|
+ color: #333;
|
|
|
+}
|
|
|
+
|
|
|
+/* 设备表格样式 */
|
|
|
+.device-table {
|
|
|
+ width: 100%;
|
|
|
+ border-collapse: separate;
|
|
|
+ border-spacing: 0;
|
|
|
+ margin-bottom: 20px;
|
|
|
+ background-color: #fff;
|
|
|
+ border-radius: 8px;
|
|
|
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
|
|
+}
|
|
|
+
|
|
|
+.device-table th {
|
|
|
+ background-color: #f8f9fa;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #333;
|
|
|
+ padding: 12px;
|
|
|
+ text-align: center;
|
|
|
+ border-bottom: 2px solid #e0e0e0;
|
|
|
+}
|
|
|
+
|
|
|
+.device-table td {
|
|
|
+ padding: 12px;
|
|
|
+ text-align: center;
|
|
|
+ border-bottom: 1px solid #e0e0e0;
|
|
|
+}
|
|
|
+
|
|
|
+.device-table tr:hover {
|
|
|
+ background-color: #f1f1f1;
|
|
|
+ transition: background-color 0.3s ease;
|
|
|
+}
|
|
|
+
|
|
|
+.device-table tr:last-child td {
|
|
|
+ border-bottom: none;
|
|
|
+}
|
|
|
+
|
|
|
+/* 绑定按钮样式 */
|
|
|
+.bind-button {
|
|
|
+ background: linear-gradient(135deg, #4caf50, #81c784);
|
|
|
+ color: white;
|
|
|
+ border: none;
|
|
|
+ padding: 8px 16px;
|
|
|
+ border-radius: 6px;
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 500;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
|
+}
|
|
|
+
|
|
|
+.bind-button:hover {
|
|
|
+ background: linear-gradient(135deg, #45a049, #6bbf70);
|
|
|
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
|
|
+}
|
|
|
+
|
|
|
+/* 查看设备按钮样式 */
|
|
|
+.view-devices-button {
|
|
|
+ background: linear-gradient(135deg, #4caf50, #81c784); /* 渐变背景 */
|
|
|
+ color: white; /* 字体颜色 */
|
|
|
+ border: none; /* 去除边框 */
|
|
|
+ padding: 8px 16px; /* 内边距 */
|
|
|
+ border-radius: 6px; /* 圆角 */
|
|
|
+ font-size: 14px; /* 字体大小 */
|
|
|
+ font-weight: 500; /* 字体粗细 */
|
|
|
+ cursor: pointer; /* 鼠标指针 */
|
|
|
+ transition: all 0.3s ease; /* 过渡效果 */
|
|
|
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); /* 阴影效果 */
|
|
|
+ margin-right: 8px; /* 右侧外边距 */
|
|
|
+}
|
|
|
+
|
|
|
+.view-devices-button:hover {
|
|
|
+ background: linear-gradient(135deg, #45a049, #6bbf70); /* 渐变背景 */
|
|
|
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); /* 阴影效果 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 编辑按钮样式 */
|
|
|
+.edit-button {
|
|
|
+ background: linear-gradient(135deg, #2196f3, #64b5f6); /* 渐变背景 */
|
|
|
+ color: white; /* 字体颜色 */
|
|
|
+ border: none; /* 去除边框 */
|
|
|
+ padding: 8px 16px; /* 内边距 */
|
|
|
+ border-radius: 6px; /* 圆角 */
|
|
|
+ font-size: 14px; /* 字体大小 */
|
|
|
+ font-weight: 500; /* 字体粗细 */
|
|
|
+ cursor: pointer; /* 鼠标指针 */
|
|
|
+ transition: all 0.3s ease; /* 过渡效果 */
|
|
|
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); /* 阴影效果 */
|
|
|
+}
|
|
|
+
|
|
|
+.edit-button:hover {
|
|
|
+ background: linear-gradient(135deg, #1e88e5, #42a5f5); /* 渐变背景 */
|
|
|
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); /* 阴影效果 */
|
|
|
+}
|
|
|
+</style>
|