今天跟着一个学长干一个项目,今日写的是一个会议室数据大屏功能代码的实现,代码如下,只上传前端代码保证项目代码的私密性

<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> 
RoomDashboard.vue

 

posted on 2025-01-08 23:33  许七安gyg  阅读(4)  评论(0)    收藏  举报