【VUE】某时间某空间占用情况效果展示,vue2+element ui实现。场景:会议室占用、教室占用等。

某时间某空间占用情况效果展示,vue2+element ui实现。场景:会议室占用、教室占用等。

场景说明:

现在需要基于vue2和el-table实现每日会议室个时间点占用情况。

已知数据:

1、会议室数据(名称,id)

2、会议计划数据(id,名称,会议室id,开始时间,结束时间)

表格展示内容:

表头构成:日期,会议室,00-07,08,09,10...19-23(小时格子对应24小时)。

1、日期列:代表从今天开始到未来7天的日期,因此一天对应多个会议室,需要合并相同日期。

2、格子占用规则:会议室时间开始时间向下取整,结束时间向上取整,跨时间合并小时格子。

3、格子显示内容:展示对应会议数据的数据量,鼠标悬浮上去需要弹出显示会议数据列表。

具体如:2025-05-21 8:23-9:00 就占08的格子,8:23-9:30就占08和09的格子并合并,

特殊情况如有3个会议数据对应时间 18-19:30,20-20:30,21-23,就需要合并18和19-23的格子并显示数量3,鼠标悬浮展示这三条。

实现效果:

具体展示效果可以根据代码调整。

代码实现:

javascript 复制代码
<template>
  <div class="meeting-room-table">
    <el-table :data="tableData" :span-method="objectSpanMethod" border tooltip-effect="light" style="width: 100%">
      <el-table-column prop="date" label="时间" width="120"></el-table-column>
      <el-table-column prop="roomName" label="会议室" width="120"></el-table-column>
      <el-table-column prop="time0007" label="00-07">
        <template slot-scope="scope">
          <el-tooltip content="" :disabled="scope.row.time0007 === 0" effect="light" placement="top">
            <div slot="content">
              <div v-if="scope.row.time0007Meetings.length" v-for="(meeting, index) in scope.row.time0007Meetings"
                :key="index" class="tooltip-item">
                <span>主持人:{{ meeting.userName }}</span>
                <span>主题: {{ meeting.theme }}</span>
                <span>会议时间:{{ formatTime(meeting.beginSta) }}-{{ formatTime(meeting.beginEnd) }}</span>
                <span>会议状态:{{meeting.state|statefitler}}</span>
              </div>
            </div>
            <div class="time-cell" :class="{ 'occupied': scope.row.time0007 > 0 }"
              @mouseenter="showTooltip(scope.$index, 'time0007')">
              <i v-if="scope.row.time0007" class="el-icon-user"/> {{ scope.row.time0007 || '' }}
            </div>
          </el-tooltip>
        </template>
      </el-table-column>
      <el-table-column prop="time08" label="08">
        <template slot-scope="scope">
          <el-tooltip content="" :disabled="scope.row.time08 === 0" effect="light" placement="top">
            <div slot="content">
              <div v-if="scope.row.time08Meetings.length" v-for="(meeting, index) in scope.row.time08Meetings"
                :key="index" class="tooltip-item">
                <span>主持人:{{ meeting.userName }}</span>
                <span>主题: {{ meeting.theme }}</span>
                <span>会议时间:{{ formatTime(meeting.beginSta) }}-{{ formatTime(meeting.beginEnd) }}</span>
                <span>会议状态:{{meeting.state|statefitler}}</span>
              </div>
            </div>
            <div class="time-cell" :class="{ 'occupied': scope.row.time08 > 0 }"
              @mouseenter="showTooltip(scope.$index, 'time08')">
              <i v-if="scope.row.time08" class="el-icon-user"/> {{ scope.row.time08 || '' }}
            </div>
          </el-tooltip>
        </template>
      </el-table-column>
      <el-table-column prop="time09" label="09">
        <template slot-scope="scope">
          <el-tooltip content="" :disabled="scope.row.time09 === 0" effect="light" placement="top">
            <div slot="content">
              <div v-if="scope.row.time09Meetings.length" v-for="(meeting, index) in scope.row.time09Meetings"
                :key="index" class="tooltip-item">
                <span>主持人:{{ meeting.userName }}</span>
                <span>主题: {{ meeting.theme }}</span>
                <span>会议时间:{{ formatTime(meeting.beginSta) }}-{{ formatTime(meeting.beginEnd) }}</span>
                <span>会议状态:{{meeting.state|statefitler}}</span>
              </div>
            </div>
            <div class="time-cell" :class="{ 'occupied': scope.row.time09 > 0 }"
              @mouseenter="showTooltip(scope.$index, 'time09')">
              <i v-if="scope.row.time09" class="el-icon-user"/> {{ scope.row.time09 || '' }}
            </div>
          </el-tooltip>
        </template>
      </el-table-column>
      <el-table-column prop="time10" label="10">
        <template slot-scope="scope">
          <el-tooltip content="" :disabled="scope.row.time10 === 0" effect="light" placement="top">
            <div slot="content">
              <div v-if="scope.row.time10Meetings.length" v-for="(meeting, index) in scope.row.time10Meetings"
                :key="index" class="tooltip-item">
                <span>主持人:{{ meeting.userName }}</span>
                <span>主题: {{ meeting.theme }}</span>
                <span>会议时间:{{ formatTime(meeting.beginSta) }}-{{ formatTime(meeting.beginEnd) }}</span>
                <span>会议状态:{{meeting.state|statefitler}}</span>
              </div>
            </div>
            <div class="time-cell" :class="{ 'occupied': scope.row.time10 > 0 }"
              @mouseenter="showTooltip(scope.$index, 'time10')">
              <i v-if="scope.row.time10" class="el-icon-user"/> {{ scope.row.time10 || '' }}
            </div>
          </el-tooltip>
        </template>
      </el-table-column>
      <el-table-column prop="time11" label="11">
        <template slot-scope="scope">
          <el-tooltip content="" :disabled="scope.row.time11 === 0" effect="light" placement="top">
            <div slot="content">
              <div v-if="scope.row.time11Meetings.length" v-for="(meeting, index) in scope.row.time11Meetings"
                :key="index" class="tooltip-item">
                <span>主持人:{{ meeting.userName }}</span>
                <span>主题: {{ meeting.theme }}</span>
                <span>会议时间:{{ formatTime(meeting.beginSta) }}-{{ formatTime(meeting.beginEnd) }}</span>
                <span>会议状态:{{meeting.state|statefitler}}</span>
              </div>
            </div>
            <div class="time-cell" :class="{ 'occupied': scope.row.time11 > 0 }"
              @mouseenter="showTooltip(scope.$index, 'time11')">
              <i v-if="scope.row.time11" class="el-icon-user"/> {{ scope.row.time11 || '' }}
            </div>
          </el-tooltip>
        </template>
      </el-table-column>
      <el-table-column prop="time12" label="12">
        <template slot-scope="scope">
          <el-tooltip content="" :disabled="scope.row.time12 === 0" effect="light" placement="top">
            <div slot="content">
              <div v-if="scope.row.time12Meetings.length" v-for="(meeting, index) in scope.row.time12Meetings"
                :key="index" class="tooltip-item">
                <span>主持人:{{ meeting.userName }}</span>
                <span>主题: {{ meeting.theme }}</span>
                <span>会议时间:{{ formatTime(meeting.beginSta) }}-{{ formatTime(meeting.beginEnd) }}</span>
                <span>会议状态:{{meeting.state|statefitler}}</span>
              </div>
            </div>
            <div class="time-cell" :class="{ 'occupied': scope.row.time12 > 0 }"
              @mouseenter="showTooltip(scope.$index, 'time12')">
              <i v-if="scope.row.time12" class="el-icon-user"/> {{ scope.row.time12 || '' }}
            </div>
          </el-tooltip>
        </template>
      </el-table-column>
      <el-table-column prop="time13" label="13">
        <template slot-scope="scope">
          <el-tooltip content="" :disabled="scope.row.time13 === 0" effect="light" placement="top">
            <div slot="content">
              <div v-if="scope.row.time13Meetings.length" v-for="(meeting, index) in scope.row.time13Meetings"
                :key="index" class="tooltip-item">
                <span>主持人:{{ meeting.userName }}</span>
                <span>主题: {{ meeting.theme }}</span>
                <span>会议时间:{{ formatTime(meeting.beginSta) }}-{{ formatTime(meeting.beginEnd) }}</span>
                <span>会议状态:{{meeting.state|statefitler}}</span>
              </div>
            </div>
            <div class="time-cell" :class="{ 'occupied': scope.row.time13 > 0 }"
              @mouseenter="showTooltip(scope.$index, 'time13')">
              <i v-if="scope.row.time13" class="el-icon-user"/> {{ scope.row.time13 || '' }}
            </div>
          </el-tooltip>
        </template>
      </el-table-column>
      <el-table-column prop="time14" label="14">
        <template slot-scope="scope">
          <el-tooltip content="" :disabled="scope.row.time14 === 0" effect="light" placement="top">
            <div slot="content">
              <div v-if="scope.row.time14Meetings.length" v-for="(meeting, index) in scope.row.time14Meetings"
                :key="index" class="tooltip-item">
                <span>主持人:</span>
                <span>主题:</span>
                <span>会议时间:</span>
                <span>会议状态:</span>
                <span>主持人:{{ meeting.userName }}</span>
                <span>主题: {{ meeting.theme }}</span>
                <span>会议时间:{{ formatTime(meeting.beginSta) }}-{{ formatTime(meeting.beginEnd) }}</span>
                <span>会议状态:{{meeting.state|statefitler}}</span>
              </div>
            </div>
            <div class="time-cell" :class="{ 'occupied': scope.row.time14 > 0 }"
              @mouseenter="showTooltip(scope.$index, 'time14')">
              <i v-if="scope.row.time14" class="el-icon-user"/> {{ scope.row.time14 || '' }}
            </div>
          </el-tooltip>
        </template>
      </el-table-column>
      <el-table-column prop="time15" label="15">
        <template slot-scope="scope">
          <el-tooltip content="" :disabled="scope.row.time15 === 0" effect="light" placement="top">
            <div slot="content">
              <div v-if="scope.row.time15Meetings.length" v-for="(meeting, index) in scope.row.time15Meetings"
                :key="index" class="tooltip-item">
                <span>主持人:{{ meeting.userName }}</span>
                <span>主题: {{ meeting.theme }}</span>
                <span>会议时间:{{ formatTime(meeting.beginSta) }}-{{ formatTime(meeting.beginEnd) }}</span>
                <span>会议状态:{{meeting.state|statefitler}}</span>
              </div>
            </div>
            <div class="time-cell" :class="{ 'occupied': scope.row.time15 > 0 }"
              @mouseenter="showTooltip(scope.$index, 'time15')">
              <i v-if="scope.row.time15" class="el-icon-user"/> {{ scope.row.time15 || '' }}
            </div>
          </el-tooltip>
        </template>
      </el-table-column>
      <el-table-column prop="time16" label="16">
        <template slot-scope="scope">
          <el-tooltip content="" :disabled="scope.row.time16 === 0" effect="light" placement="top">
            <div slot="content">
              <div v-if="scope.row.time16Meetings.length" v-for="(meeting, index) in scope.row.time16Meetings"
                :key="index" class="tooltip-item">
                <span>主持人:{{ meeting.userName }}</span>
                <span>主题: {{ meeting.theme }}</span>
                <span>会议时间:{{ formatTime(meeting.beginSta) }}-{{ formatTime(meeting.beginEnd) }}</span>
                <span>会议状态:{{meeting.state|statefitler}}</span>
              </div>
            </div>
            <div class="time-cell" :class="{ 'occupied': scope.row.time16 > 0 }"
              @mouseenter="showTooltip(scope.$index, 'time16')">
              <i v-if="scope.row.time16" class="el-icon-user"/> {{ scope.row.time16 || '' }}
            </div>
          </el-tooltip>
        </template>
      </el-table-column>
      <el-table-column prop="time17" label="17">
        <template slot-scope="scope">
          <el-tooltip content="" :disabled="scope.row.time17 === 0" effect="light" placement="top">
            <div slot="content">
              <div v-if="scope.row.time17Meetings.length" v-for="(meeting, index) in scope.row.time17Meetings"
                :key="index" class="tooltip-item">
                <span>主持人:{{ meeting.userName }}</span>
                <span>主题: {{ meeting.theme }}</span>
                <span>会议时间:{{ formatTime(meeting.beginSta) }}-{{ formatTime(meeting.beginEnd) }}</span>
                <span>会议状态:{{meeting.state|statefitler}}</span>
              </div>
            </div>
            <div class="time-cell" :class="{ 'occupied': scope.row.time17 > 0 }"
              @mouseenter="showTooltip(scope.$index, 'time17')">
              <i v-if="scope.row.time17" class="el-icon-user"/> {{ scope.row.time17 || '' }}
            </div>
          </el-tooltip>
        </template>
      </el-table-column>
      <el-table-column prop="time18" label="18">
        <template slot-scope="scope">
          <el-tooltip content="" :disabled="scope.row.time18 === 0" effect="light" placement="top">
            <div slot="content">
              <div v-if="scope.row.time18Meetings.length" v-for="(meeting, index) in scope.row.time18Meetings"
                :key="index" class="tooltip-item">
                <span>主持人:{{ meeting.userName }}</span>
                <span>主题: {{ meeting.theme }}</span>
                <span>会议时间:{{ formatTime(meeting.beginSta) }}-{{ formatTime(meeting.beginEnd) }}</span>
                <span>会议状态:{{meeting.state|statefitler}}</span>
              </div>
            </div>
            <div class="time-cell" :class="{ 'occupied': scope.row.time18 > 0 }"
              @mouseenter="showTooltip(scope.$index, 'time18')">
              <i v-if="scope.row.time18" class="el-icon-user"/> {{ scope.row.time18 || '' }}
            </div>
          </el-tooltip>
        </template>
      </el-table-column>
      <el-table-column prop="time1923" label="19-23">
        <template slot-scope="scope">
          <el-tooltip content="" :disabled="scope.row.time1923 === 0" effect="light" placement="top">
            <div slot="content">
              <div v-if="scope.row.time1923Meetings.length" v-for="(meeting, index) in scope.row.time1923Meetings"
                :key="index" class="tooltip-item">
                <span>主持人:{{ meeting.userName }}</span>
                <span>主题: {{ meeting.theme }}</span>
                <span>会议时间:{{ formatTime(meeting.beginSta) }}-{{ formatTime(meeting.beginEnd) }}</span>
                <span>会议状态:{{meeting.state|statefitler}}</span>
              </div>
            </div>
            <div class="time-cell" :class="{ 'occupied': scope.row.time1923 > 0 }"
              @mouseenter="showTooltip(scope.$index, 'time1923')">
              <i v-if="scope.row.time1923" class="el-icon-user"/> {{ scope.row.time1923 || '' }}
            </div>
          </el-tooltip>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>

<script>
  //按需引入接口
 // import {listMeeting,listMeetingRoom } from "@/api/meeting";
  export default {
    name: 'MeetingRoomTable',
    data() {
      return {
        rooms: [],
        meetings: [],
        tableData: [],
        dateSpanArr: [],
        roomSpanArr: [],
        meetingSpanMap: new Map() // 存储会议合并信息
      }
    },
    //可作为组件接收外部入参
    props: {
      roomList: {
        type: Array,
        default: null
      },
      meetingList: {
        type: Array,
        default: null
      }
    },
    filters: {
      statefitler(value) {
        if (value == '1') {
          return '召开中';
        }
        if (value == '2') {
          return '已结束';
        }
        if (value == '3') {
          return '待召开';
        }
        if (value == '4') {
          return '已取消';
        }
      }
    },
    watch: {
      roomList: {
        handler(newVal) {
          if(newVal){
            this.rooms = newVal;
            this.generateTableData();
          }
        },
        deep: true,
        immediate: true
      },
      meetingList: {
        handler(newVal) {
          if(newVal){
            this.meetings = newVal;
            this.generateTableData();
          }
        },
        deep: true,
        immediate: true
      }
    },
    created() {
      // 如果没有传入数据,使用默认数据
      if (this.roomList == null) {
        // this.getRealeRooms()
         this.rooms = this.getDefaultRooms();
      }

      if (this.meetingList == null) {
        // this.getRealeMeetings()
         this.meetings = this.getDefaultMeetings();
      }
      setTimeout(()=>{this.generateTableData()},500)

    },
    methods: {
      generateTableData() {
        // 初始化会议合并信息
        this.meetingSpanMap.clear();

        // 生成未来7天日期
        const dates = [];
        for (let i = 0; i < 7; i++) {
          const date = new Date();
          date.setDate(date.getDate() + i);
          dates.push(this.formatDate(date));
        }

        // 时间段映射
        const timeSlotMap = {
          0: 'time0007',
          1: 'time0007',
          2: 'time0007',
          3: 'time0007',
          4: 'time0007',
          5: 'time0007',
          6: 'time0007',
          7: 'time0007',
          8: 'time08',
          9: 'time09',
          10: 'time10',
          11: 'time11',
          12: 'time12',
          13: 'time13',
          14: 'time14',
          15: 'time15',
          16: 'time16',
          17: 'time17',
          18: 'time18',
          19: 'time1923',
          20: 'time1923',
          21: 'time1923',
          22: 'time1923',
          23: 'time1923'
        };

        // 初始化表格数据
        this.tableData = [];
        dates.forEach(date => {
          this.rooms.forEach(room => {
            this.tableData.push({
              date,
              meetingRoom: room.id,
              roomName: room.name,
              time0007: 0,
              time08: 0,
              time09: 0,
              time10: 0,
              time11: 0,
              time12: 0,
              time13: 0,
              time14: 0,
              time15: 0,
              time16: 0,
              time17: 0,
              time18: 0,
              time1923: 0,
              time0007Meetings: [],
              time08Meetings: [],
              time09Meetings: [],
              time10Meetings: [],
              time11Meetings: [],
              time12Meetings: [],
              time13Meetings: [],
              time14Meetings: [],
              time15Meetings: [],
              time16Meetings: [],
              time17Meetings: [],
              time18Meetings: [],
              time1923Meetings: []
            });
          });
        });

        // 处理会议数据
        this.meetings.forEach(meeting => {
          // 查找对应的表格行
          const rowIndex = this.tableData.findIndex(item => {
            return item.date === this.formatDate(new Date(meeting.beginSta)) && item.meetingRoom == meeting.meetingRoom;
          });

          if (rowIndex !== -1) {
            // 计算会议占用的时间段
            const startHour = new Date(meeting.beginSta).getHours();
            const endHour = new Date(meeting.beginEnd).getHours();
            const startMinute = new Date(meeting.beginSta).getMinutes();
            const endMinute = new Date(meeting.beginEnd).getMinutes();

            // 开始时间:如果分钟数大于0,占用下一个完整小时
            // 结束时间:如果分钟数大于0,占用当前小时;否则占用前一小时
            let startTimeSlot = startHour;
            let endTimeSlot = endHour + (endMinute > 0 ? 0 : -1);
            // 记录该会议占用的时间段
            const timeSlots = new Set();

            // 生成所有被占用的时间段
            for (let hour = startTimeSlot; hour <= endTimeSlot; hour++) {
              if (hour in timeSlotMap) {
                timeSlots.add(timeSlotMap[hour]);
              }
            }

            // 更新表格数据和合并信息
            if (timeSlots.size > 0) {
              // 转换为有序数组
              const orderedTimeSlots = Array.from(timeSlots);
              // 记录时间段与会议的映射关系
              orderedTimeSlots.forEach(slot => {
                // 更新占用计数
                this.tableData[rowIndex][slot]++; // 这里是关键:对每个时间段的占用计数+1
                // 添加会议信息
                this.tableData[rowIndex][`${slot}Meetings`].push(meeting);
                console.log(slot, this.tableData[rowIndex][slot])
              });

              // 记录会议合并信息
              if (orderedTimeSlots.length > 1) {
                this.meetingSpanMap.set(`${rowIndex}-${meeting.meetingId}`, orderedTimeSlots);
              }
            }
          }
        });
        // 处理合并单元格
        this.handleSpan();
        console.log("tableData", this.tableData)
      },
      handleSpan() {
        // 重置合并数组
        this.dateSpanArr = [];
        this.roomSpanArr = [];
        let datePos = 0;
        let roomPos = 0;

        // 处理日期列合并
        this.tableData.forEach((item, index) => {
          if (index === 0) {
            this.dateSpanArr.push(1);
            datePos = 0;
          } else {
            // 如果当前行日期和上一行日期相同,则合并
            if (item.date === this.tableData[index - 1].date) {
              this.dateSpanArr[datePos] += 1;
              this.dateSpanArr.push(0);
            } else {
              this.dateSpanArr.push(1);
              datePos = index;
            }
          }
        });

        // 处理会议室列合并
        this.tableData.forEach((item, index) => {
          if (index === 0) {
            this.roomSpanArr.push(1);
            roomPos = 0;
          } else {
            // 如果当前行日期和上一行日期相同,且会议室也相同,则合并
            if (item.date === this.tableData[index - 1].date && item.meetingRoom === this.tableData[index - 1].meetingRoom) {
              this.roomSpanArr[roomPos] += 1;
              this.roomSpanArr.push(0);
            } else {
              this.roomSpanArr.push(1);
              roomPos = index;
            }
          }
        });
        //===========================合并格子进行会议数据合并去重共享============================================
        // 时间段列
          const timeProps = [
            'time0007', 'time08', 'time09', 'time10', 'time11',
            'time12', 'time13', 'time14', 'time15', 'time16',
            'time17', 'time18', 'time1923'
          ];

          // 处理时间段列的会议数据合并
          this.tableData.forEach((row, rowIndex) => {
            // 检查是否有需要合并的会议
            const meetingsToMerge = [];

            // 遍历所有时间段,收集需要合并的会议
            timeProps.forEach(timeProp => {
              const meetings = row[`${timeProp}Meetings`] || [];

              meetings.forEach(meeting => {
                // 如果会议已经在列表中,跳过
                if (meetingsToMerge.some(m => m.meetingId === meeting.meetingId)) {
                  return;
                }

                // 计算会议覆盖的时间段
                const startHour = new Date(meeting.beginSta).getHours();
                const endHour = new Date(meeting.beginEnd).getHours();
                const endMinute = new Date(meeting.beginEnd).getMinutes();

                // 确定会议覆盖的时间段索引范围
                const startTimeSlot = this.getTimeSlotIndex(startHour);
                const endTimeSlot = this.getTimeSlotIndex(endHour + (endMinute > 0 ? 0 : -1));

                // 如果会议跨多个时间段,添加到合并列表
                if (endTimeSlot > startTimeSlot) {
                  meetingsToMerge.push({
                    ...meeting,
                    startTimeSlot,
                    endTimeSlot
                  });
                }
              });
            });

            // 处理需要合并的会议
            meetingsToMerge.forEach(meeting => {
              // 收集会议覆盖的所有时间段的会议数据
              const allMeetings = [];
              const meetingIds = new Set();

              for (let i = meeting.startTimeSlot; i <= meeting.endTimeSlot; i++) {
                const timeProp = timeProps[i];
                const slotMeetings = row[`${timeProp}Meetings`] || [];

                slotMeetings.forEach(m => {
                  if (!meetingIds.has(m.meetingId)) {
                    meetingIds.add(m.meetingId);
                    allMeetings.push(m);
                  }
                });
              }

              // 更新所有相关时间段的会议数据
              for (let i = meeting.startTimeSlot; i <= meeting.endTimeSlot; i++) {
                const timeProp = timeProps[i];
                row[timeProp] = allMeetings.length;
                row[`${timeProp}Meetings`] = allMeetings;
              }
            });
          });
          //===========================合并格子进行会议数据合并去重共享============================================
      },
      // 根据小时获取时间段索引
      getTimeSlotIndex(hour) {
        const timeSlotMap = {
          0: 0, 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, // time0007
          8: 1, 9: 2, 10: 3, 11: 4, // time08, time09, time10, time11
          12: 5, 13: 6, 14: 7, 15: 8, 16: 9, 17: 10, 18: 11, // time12-time18
          19: 12, 20: 12, 21: 12, 22: 12, 23: 12 // time1923
        };

        return timeSlotMap[hour] || 0;
      },
      //el-table表格合并方法
      objectSpanMethod({
        row,
        column,
        rowIndex,
        columnIndex
      }) {
        // 处理日期列和会议室列的合并
        if (columnIndex === 0) {
          return {
            rowspan: this.dateSpanArr[rowIndex],
            colspan: 1
          };
        } else if (columnIndex === 1) {
          return {
            rowspan: this.roomSpanArr[rowIndex],
            colspan: 1
          };
        }

        // 处理时间段列的会议合并
        const timePropIndex = columnIndex - 2; // 时间段列从第3列开始
        if (timePropIndex < 0) return {
          rowspan: 1,
          colspan: 1
        };

        const timeProps = [
          'time0007', 'time08', 'time09', 'time10', 'time11',
          'time12', 'time13', 'time14', 'time15', 'time16',
          'time17', 'time18', 'time1923'
        ];

        const timeProp = timeProps[timePropIndex];
        if (!timeProp) return {
          rowspan: 1,
          colspan: 1
        };

        // 检查当前单元格是否属于某个需要合并的会议
        for (const [key, timeSlots] of this.meetingSpanMap.entries()) {
          const [rowIdx, meetingId] = key.split('-');
          if (parseInt(rowIdx) === rowIndex && timeSlots.includes(timeProp)) {
            const firstSlot = timeSlots[0];
            const lastSlot = timeSlots[timeSlots.length - 1];
            // 如果是会议的第一个时间段,设置合并
            if (timeProp === firstSlot) {
              // 找出合并范围内数量最多的时间段
              const timePropIdx = timeProps.indexOf(timeProp);
              const lastSlotIdx = timeProps.indexOf(lastSlot);
              return {
                rowspan: 1,
                colspan: lastSlotIdx - timePropIdx + 1
              };
            } else {
              // 否则不显示
              return {
                rowspan: 0,
                colspan: 0
              };
            }
          }
        }

        // 默认不合并
        return {
          rowspan: 1,
          colspan: 1
        };
      },
      formatDate(date) {
        const year = date.getFullYear();
        const month = String(date.getMonth() + 1).padStart(2, '0');
        const day = String(date.getDate()).padStart(2, '0');
        return `${year}-${month}-${day}`;
      },
      formatTime(dateTime) {
        const date = new Date(dateTime);
        const hour = String(date.getHours()).padStart(2, '0');
        const minute = String(date.getMinutes()).padStart(2, '0');
        return `${hour}:${minute}`;
      },
      showTooltip(rowIndex, timeKey) {
        // 处理鼠标悬停事件
      },
      //会议室数据
      getRealeRooms() {
        //获取真实数据接口
        listMeetingRoom().then(response => {
          this.rooms = response.rows;
          this.generateTableData();
        });
      },
      //会议数据
      getRealeMeetings() {
        //获取今天及今天之后的数据
        const today = new Date();
        listMeeting().then(response => {
          this.meetings = response.rows;
          this.generateTableData();
        });
      },
      getDefaultRooms() {
        return [{
            id: 1,
            name: '会议室A'
          },
          {
            id: 2,
            name: '会议室B'
          },
          {
            id: 3,
            name: '会议室C'
          }
        ];
      },
      getDefaultMeetings() {
        const today = new Date();
        return [{
            meetingId: 1,
            theme: '周会',
            userName: '张三',
            meetingRoom: 1,
            state:1,
            beginSta: new Date(today.setHours(4, 30, 0)).toISOString(),
            beginEnd: new Date(today.setHours(5, 0, 0)).toISOString()
          },
          {
            meetingId: 2,
            theme: '技审',
            userName: '张三',
            meetingRoom: 1,
            state:1,
            beginSta: new Date(today.setHours(6, 0, 0)).toISOString(),
            beginEnd: new Date(today.setHours(9, 30, 0)).toISOString()
          },
          {
            meetingId: 3,
            theme: '技审3',
            userName: '张三',
            meetingRoom: 1,
            state:2,
            beginSta: new Date(today.setHours(8, 0, 0)).toISOString(),
            beginEnd: new Date(today.setHours(9, 30, 0)).toISOString()
          },
          {
            meetingId: 6,
            theme: '跨时1',
            userName: '张三',
            meetingRoom: 3,
            state:2,
            beginSta: new Date(today.setHours(18, 0, 0)).toISOString(),
            beginEnd: new Date(today.setHours(20, 30, 0)).toISOString()
          },
          {
            meetingId: 7,
            theme: '跨时2',
            userName: '张三',
            meetingRoom: 3,
            state:1,
            beginSta: new Date(today.setHours(20, 0, 0)).toISOString(),
            beginEnd: new Date(today.setHours(21, 30, 0)).toISOString()
          },
          {
            meetingId: 9,
            theme: '跨19-23',
            userName: '张三',
            meetingRoom: 3,
            state:3,
            beginSta: new Date(today.setHours(20, 30, 0)).toISOString(),
            beginEnd: new Date(today.setHours(22, 30, 0)).toISOString()
          },
          {
            meetingId: 10,
            theme: '跨列测试1',
            userName: '张三',
            meetingRoom: 3,
            state:4,
            beginSta: new Date(today.setHours(18, 0, 0)).toISOString(),
            beginEnd: new Date(today.setHours(19, 30, 0)).toISOString()
          },
          {
            meetingId: 11,
            theme: '跨列测试2',
            userName: '张三',
            meetingRoom: 2,
            state:1,
            beginSta: new Date(today.setHours(20, 0, 0)).toISOString(),
            beginEnd: new Date(today.setHours(21, 0, 0)).toISOString()
          },
          {
            meetingId: 12,
            theme: '重叠测试1',
            userName: '张三',
            meetingRoom: 3,
            state:1,
            beginSta: new Date(today.setHours(19, 0, 0)).toISOString(),
            beginEnd: new Date(today.setHours(20, 30, 0)).toISOString()
          },
          {
            meetingId: 13,
            theme: '重叠测试2',
            userName: '张三',
            meetingRoom: 3,
            state:1,
            beginSta: new Date(today.setHours(20, 0, 0)).toISOString(),
            beginEnd: new Date(today.setHours(21, 30, 0)).toISOString()
          }
        ];
      }
    }
  }
</script>

<style scoped>
  .meeting-room-table {
    margin: 20px;
  }

  .time-cell {
    text-align: center;
    padding: 5px;
    height: 100%;
    border-radius: 4px;
    cursor: pointer;
  }

  .time-cell.occupied {
    background-color: #55aaff;
    color: white;
  }

  .tooltip-item {
    padding: 5px 0;
    display: flex;
    align-content: flex-start;
    flex-direction: column;
    font-size: 14px;
    border-bottom: 1px solid #eee;
  }

  .tooltip-item:last-child {
    border-bottom: none;
  }
</style>

参考DeepSeek和豆包AI。

相关推荐
Codebee39 分钟前
OneCode核心概念解析——View(视图)
前端·人工智能
GIS之路39 分钟前
GIS 数据质检:验证 Geometry 有效性
前端
GIS之路44 分钟前
GeoJSON 数据简介
前端
今阳44 分钟前
鸿蒙开发笔记-16-应用间跳转
android·前端·harmonyos
前端小饭桌1 小时前
CSS属性值太多记不住?一招教你搞定
前端·css
快起来别睡了1 小时前
深入浏览器底层原理:从输入URL到页面显示全过程解析
前端·架构
阿星做前端1 小时前
一个倒计时功能引发的线上故障
前端·javascript·react.js
莯炗1 小时前
CSS知识补充 --- 控制继承
前端·css·css继承·css控制继承
tianzhiyi1989sq1 小时前
Vue框架深度解析:从Vue2到Vue3的技术演进与实践指南
前端·javascript·vue.js
秉承初心1 小时前
webpack和vite对比解析(AI)
前端·webpack·node.js