今天跟着一个学长干一个项目,今日写的是一个会议室数据大屏功能代码的实现,代码如下,只上传前端代码保证项目代码的私密性
<template> <div class="dashboard-container"> <!-- 顶部统计卡片 --> <div class="stat-cards"> <el-row :gutter="20"> <el-col :span="6"> <el-card shadow="hover"> <div class="stat-item"> <div class="stat-title">总会议室数</div> <div class="stat-number">{{ totalRooms }}</div> </div> </el-card> </el-col> <el-col :span="6"> <el-card shadow="hover"> <div class="stat-item"> <div class="stat-title">开放中</div> <div class="stat-number">{{ openRooms }}</div> </div> </el-card> </el-col> <el-col :span="6"> <el-card shadow="hover"> <div class="stat-item"> <div class="stat-title">关闭中</div> <div class="stat-number">{{ closedRooms }}</div> </div> </el-card> </el-col> <el-col :span="6"> <el-card shadow="hover"> <div class="stat-item"> <div class="stat-title">今日预约数</div> <div class="stat-number">{{ todayOrders }}</div> </div> </el-card> </el-col> </el-row> </div> <!-- 会议室状态列表 --> <el-row :gutter="20" style="margin-top: 20px;"> <el-col :span="12"> <el-card shadow="hover"> <div slot="header"> <span>会议室使用状态</span> </div> <div class="room-list"> <div v-for="room in rooms" :key="room.rid" class="room-item" ref="roomItems" @mouseenter="handleRoomHover(room, $event)" @mouseleave="handleRoomLeave"> <div class="room-info"> <div class="room-name">{{ room.rname }}</div> <div class="room-status"> <el-tag :type="room.orgid === 1 ? 'success' : 'danger'" size="small"> {{ room.orgid === 1 ? '开放中' : '关闭中' }} </el-tag> </div> </div> <div class="room-details"> <span>地址: {{ room.raddress }}</span> <span>可用时间: {{ room.rcanbeusetimes }}</span> </div> </div> </div> </el-card> </el-col> <el-col :span="12"> <el-card shadow="hover" class="order-card"> <div slot="header"> <span>今日会议室使用情况</span> </div> <div class="order-list"> <el-table :data="todayOrderList" style="width: 100%" :max-height="400" :header-cell-style="{ background: '#f5f7fa', color: '#606266' }"> <el-table-column prop="rname" label="会议室" width="120"> </el-table-column> <el-table-column prop="usingtime" label="使用时间" width="180"> </el-table-column> <el-table-column prop="uname" label="预约人" width="120"> </el-table-column> <el-table-column prop="roomusage" label="用途" width="150"> </el-table-column> <el-table-column prop="ostatus" label="状态" width="100"> <template slot-scope="scope"> <el-tag :type="getStatusType(scope.row.ostatus)" size="small"> {{ getStatusText(scope.row.ostatus) }} </el-tag> </template> </el-table-column> </el-table> </div> </el-card> </el-col> </el-row> <!-- 悬停弹出框 --> <el-popover v-model="popoverVisible" placement="top-start" width="500" trigger="manual" :popper-class="'room-popover'" v-if="currentRoom"> <div class="popover-content" @mouseenter="handlePopoverEnter" @mouseleave="handlePopoverLeave"> <div class="popover-title">{{ currentRoom.rname }} - 预约情况</div> <el-table :data="roomOrders" size="small" style="width: 100%" :max-height="300" :header-cell-style="{ background: '#f5f7fa', color: '#606266' }"> <el-table-column prop="usingtime" label="使用时间" width="150" :show-overflow-tooltip="true"> </el-table-column> <el-table-column prop="uname" label="预约人" width="100" :show-overflow-tooltip="true"> </el-table-column> <el-table-column prop="roomusage" label="用途" width="150" :show-overflow-tooltip="true"> </el-table-column> <el-table-column prop="ostatus" label="状态" width="100"> <template slot-scope="scope"> <el-tag :type="getStatusType(scope.row.ostatus)" size="mini"> {{ getStatusText(scope.row.ostatus) }} </el-tag> </template> </el-table-column> </el-table> <div v-if="roomOrders.length === 0" class="no-data"> 暂无预约记录 </div> </div> <div slot="reference"></div> </el-popover> </div> </template> <script> export default { name: 'RoomDashboard', data() { return { rooms: [], todayOrderList: [], totalRooms: 0, openRooms: 0, closedRooms: 0, todayOrders: 0, popoverVisible: false, currentRoom: null, roomOrders: [], hoverTimer: null, currentElement: null, isPopoverHovered: false } }, methods: { // 加载会议室数据 loadRoomsData() { this.$axios.get(this.$httpUrl + '/rooms/dashboard').then(res => { if (res.data.code === 200) { this.rooms = res.data.data.rooms || []; this.totalRooms = this.rooms.length; this.openRooms = this.rooms.filter(room => room.orgid === 1).length; this.closedRooms = this.totalRooms - this.openRooms; } }); }, // 加载今日预约数据 loadTodayOrders() { this.$axios.get(this.$httpUrl + '/orderitems/today').then(res => { if (res.data.code === 200) { this.todayOrderList = res.data.data || []; this.todayOrders = this.todayOrderList.length; } }); }, // 获取状态样式 getStatusType(status) { switch (status) { case 1: return 'warning'; case 2: return 'danger'; case 3: return 'success'; default: return 'info'; } }, // 获取状态文本 getStatusText(status) { switch (status) { case 1: return '审批中'; case 2: return '已拒绝'; case 3: return '已通过'; default: return '未知'; } }, // 处理会议室悬停 handleRoomHover(room, event) { if (this.hoverTimer) clearTimeout(this.hoverTimer); // 保存当前元素引用 this.currentElement = event.currentTarget; this.hoverTimer = setTimeout(() => { if (!this.currentElement) return; const rect = this.currentElement.getBoundingClientRect(); this.currentRoom = room; this.loadRoomOrders(room.rid); // 设置弹出框位置 this.$nextTick(() => { const popover = document.querySelector('.room-popover'); if (popover) { const viewportWidth = window.innerWidth; const viewportHeight = window.innerHeight; // 根据空间自动调整位置 if (rect.right + 500 > viewportWidth) { popover.style.left = (rect.left - 500) + 'px'; } if (rect.top + 300 > viewportHeight) { popover.style.top = (rect.top - 300) + 'px'; } } }); }, 500); }, // 处理会议室离开 handleRoomLeave() { if (this.hoverTimer) { clearTimeout(this.hoverTimer); this.hoverTimer = null; } this.currentElement = null; // 如果鼠标不在弹框上,才关闭弹框 if (!this.isPopoverHovered) { setTimeout(() => { this.popoverVisible = false; this.currentRoom = null; this.roomOrders = []; }, 200); } }, // 处理弹框悬停 handlePopoverEnter() { this.isPopoverHovered = true; }, // 处理弹框离开 handlePopoverLeave() { this.isPopoverHovered = false; setTimeout(() => { if (!this.currentElement) { // 如果不在原始元素上 this.popoverVisible = false; this.currentRoom = null; this.roomOrders = []; } }, 200); }, // 加载会议室预约数据 loadRoomOrders(rid) { this.$axios.get(this.$httpUrl + `/orderitems/room/${rid}`).then(res => { if (res.data.code === 200) { this.roomOrders = res.data.data || []; this.popoverVisible = true; } }); } }, created() { this.loadRoomsData(); this.loadTodayOrders(); // 定时刷新数据 setInterval(() => { this.loadRoomsData(); this.loadTodayOrders(); }, 30000); // 每30秒刷新一次 } } </script> <style scoped> .dashboard-container { padding: 20px; } .stat-cards { margin-bottom: 20px; } .stat-item { text-align: center; } .stat-title { font-size: 16px; color: #606266; margin-bottom: 10px; } .stat-number { font-size: 24px; font-weight: bold; color: #303133; } .room-list { max-height: 500px; overflow-y: auto; } .room-item { padding: 15px; border-bottom: 1px solid #EBEEF5; } .room-item:last-child { border-bottom: none; } .room-info { display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; } .room-name { font-size: 16px; font-weight: bold; } .room-details { display: flex; justify-content: space-between; color: #909399; font-size: 14px; } .order-card { height: 500px; display: flex; flex-direction: column; } .order-card >>> .el-card__body { flex: 1; overflow: hidden; padding: 0; } .order-list { height: 100%; overflow-y: auto; } .order-list .el-table { height: 100%; } /* 自定义滚动条样式 */ .order-list::-webkit-scrollbar { width: 6px; } .order-list::-webkit-scrollbar-thumb { background-color: #909399; border-radius: 3px; } .order-list::-webkit-scrollbar-track { background-color: #f5f7fa; } /* 表格内容紧凑一些 */ .order-list .el-table td, .order-list .el-table th { padding: 8px 0; } /* 表格hover效果 */ .order-list .el-table--enable-row-hover .el-table__body tr:hover > td { background-color: #f5f7fa; } .room-popover { padding: 0; box-shadow: 0 2px 12px 0 rgba(0,0,0,.1); border-radius: 4px; pointer-events: auto !important; } .popover-title { padding: 12px 15px; font-weight: bold; border-bottom: 1px solid #EBEEF5; background-color: #f5f7fa; color: #303133; font-size: 14px; position: sticky; top: 0; z-index: 1; } .popover-content { max-height: 300px; overflow-y: auto; padding: 0; pointer-events: auto; } .popover-content .el-table { margin: 0; } /* 自定义滚动条样式 */ .popover-content::-webkit-scrollbar { width: 6px; } .popover-content::-webkit-scrollbar-thumb { background-color: #909399; border-radius: 3px; } .popover-content::-webkit-scrollbar-track { background-color: #f5f7fa; } .no-data { padding: 20px; text-align: center; color: #909399; font-size: 14px; } /* 表格内容紧凑一些 */ .popover-content .el-table td, .popover-content .el-table th { padding: 8px 0; } /* 表格hover效果 */ .popover-content .el-table--enable-row-hover .el-table__body tr:hover > td { background-color: #f5f7fa; } /* 确保弹出框在其他元素之上 */ .room-popover { z-index: 2000 !important; } </style>
浙公网安备 33010602011771号