From 41c9d91da0c73303ea6f8eae03f030ce28b6cd1d Mon Sep 17 00:00:00 2001
From: zhangwencui <1064582902@qq.com>
Date: 星期五, 16 一月 2026 16:08:02 +0800
Subject: [PATCH] 会议列表时间段修改

---
 src/pages/managementMeetings/meetingList/index.vue |  537 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 537 insertions(+), 0 deletions(-)

diff --git a/src/pages/managementMeetings/meetingList/index.vue b/src/pages/managementMeetings/meetingList/index.vue
new file mode 100644
index 0000000..ad8a0de
--- /dev/null
+++ b/src/pages/managementMeetings/meetingList/index.vue
@@ -0,0 +1,537 @@
+<template>
+  <view class="meeting-list">
+    <PageHeader title="浼氳鍒楄〃"
+                @back="goBack" />
+    <!-- 鏌ヨ琛ㄥ崟 -->
+    <view class="search-section">
+      <view class="search-bar">
+        <view class="search-input">
+          <up-input class="search-text"
+                    placeholder="鏌ヨ鏃ユ湡"
+                    @click.stop="showDatePicker"
+                    v-model="queryForm.meetingDate"
+                    clearable />
+        </view>
+        <view class="filter-button"
+              @click="clearDate">
+          <u-icon name="close-circle-fill"
+                  size="24"
+                  color="#999"></u-icon>
+        </view>
+      </view>
+    </view>
+    <!-- 鏃ユ湡閫夋嫨鍣� -->
+    <up-datetime-picker v-model="datePickerValue"
+                        mode="date"
+                        :show="showDatePickerDialog"
+                        @confirm="handleDateConfirm"
+                        @cancel="showDatePickerDialog = false"
+                        format="YYYY-MM-DD"
+                        value-format="YYYY-MM-DD" />
+    <!-- 浼氳瀹や娇鐢ㄦ儏鍐� -->
+    <view class="table-container">
+      <scroll-view scroll-x="true"
+                   style="width: 100%;">
+        <view class="time-table">
+          <!-- 琛ㄥご -->
+          <view class="table-header">
+            <view class="header-cell room-header">浼氳瀹�</view>
+            <view v-for="timeSlot in timeSlots"
+                  :key="timeSlot.value"
+                  class="header-cell time-header">
+              {{ timeSlot.label }}
+            </view>
+          </view>
+          <!-- 琛ㄦ牸鍐呭 -->
+          <view class="table-body">
+            <view v-for="room in roomUsage"
+                  :key="room.id"
+                  class="table-row">
+              <view class="cell room-cell">{{ room.name }}</view>
+              <view class="cells-container">
+                <template v-for="(cell, index) in generateMeetingCells(room)"
+                          :key="index">
+                  <view class="cell content-cell"
+                        :class="[cell.type, `status-${cell.meeting?.status || '0'}`]"
+                        :style="{ width: `${cell.span * 120}rpx` }"
+                        @click="viewMeetingDetails(cell)">
+                    <view v-if="cell.type === 'meeting'"
+                          class="meeting-content">
+                      <view class="meeting-title">{{ cell.meeting.title }}</view>
+                      <view class="meeting-time">{{ cell.startTime }}-{{ cell.endTime }}</view>
+                    </view>
+                    <view v-else
+                          class="free-content">
+                      绌洪棽
+                    </view>
+                  </view>
+                </template>
+              </view>
+            </view>
+          </view>
+        </view>
+      </scroll-view>
+    </view>
+    <!-- 浼氳璇︽儏瀵硅瘽妗� -->
+    <u-popup :show="detailDialogVisible"
+             mode="center"
+             customStyle="width: 80%;"
+             :round="10">
+      <view class="dialog-content">
+        <view class="dialog-header">
+          <text class="dialog-title">浼氳璇︽儏</text>
+          <up-icon name="close"
+                   @click="detailDialogVisible = false"
+                   class="close-icon"></up-icon>
+        </view>
+        <view v-if="currentMeeting"
+              class="dialog-body">
+          <view class="detail-item">
+            <text class="detail-label">浼氳涓婚</text>
+            <text class="detail-value">{{ currentMeeting.title }}</text>
+          </view>
+          <view class="detail-item">
+            <text class="detail-label">浼氳瀹�</text>
+            <text class="detail-value">{{ currentMeeting.room }}</text>
+          </view>
+          <view class="detail-item">
+            <text class="detail-label">浼氳鏃堕棿</text>
+            <text class="detail-value">{{ currentMeeting.time }}</text>
+          </view>
+          <view class="detail-item">
+            <text class="detail-label">涓绘寔浜�</text>
+            <text class="detail-value">{{ currentMeeting.host }}</text>
+          </view>
+          <view class="detail-item">
+            <text class="detail-label">鍙備細浜烘暟</text>
+            <text class="detail-value">{{ currentMeeting.participants }}浜�</text>
+          </view>
+          <view class="detail-item">
+            <text class="detail-label">浼氳璇存槑</text>
+            <text class="detail-value">{{ currentMeeting.description }}</text>
+          </view>
+        </view>
+        <view class="dialog-footer">
+          <u-button @click="detailDialogVisible = false">鍏抽棴</u-button>
+        </view>
+      </view>
+    </u-popup>
+  </view>
+</template>
+
+<script setup>
+  import { ref, reactive, onMounted } from "vue";
+  import PageHeader from "@/components/PageHeader.vue";
+  import { getMeetingUseList } from "@/api/managementMeetings/meeting.js";
+  import dayjs from "dayjs";
+
+  // 鏌ヨ琛ㄥ崟
+  const queryForm = reactive({
+    meetingDate: dayjs().format("YYYY-MM-DD"),
+  });
+
+  const loading = ref(false);
+  const timeSlots = ref([]);
+  const roomUsage = ref([]);
+  const currentMeeting = ref(null);
+  const detailDialogVisible = ref(false);
+  const showDatePickerDialog = ref(false);
+  const datePickerValue = ref(Date.now());
+
+  // 杩斿洖涓婁竴椤�
+  const goBack = () => {
+    uni.navigateBack();
+  };
+
+  // 娓呯┖鏃ユ湡
+  const clearDate = () => {
+    datePickerValue.value = Date.now();
+    queryForm.meetingDate = dayjs().format("YYYY-MM-DD");
+    getList();
+  };
+
+  // 鏄剧ず鏃ユ湡閫夋嫨鍣�
+  const showDatePicker = () => {
+    if (queryForm.meetingDate) {
+      datePickerValue.value = new Date(queryForm.meetingDate).getTime();
+    } else {
+      datePickerValue.value = Date.now();
+    }
+    console.log(datePickerValue.value, "datePickerValue.value");
+    showDatePickerDialog.value = true;
+  };
+
+  // 澶勭悊鏃ユ湡閫夋嫨纭
+  const handleDateConfirm = value => {
+    // dayjs().format("YYYY-MM-DD")
+    console.log(value, "value");
+
+    queryForm.meetingDate = dayjs(value.value).format("YYYY-MM-DD");
+    showDatePickerDialog.value = false;
+    getList();
+  };
+
+  // 鑾峰彇鍒楄〃鏁版嵁
+  const getList = () => {
+    handleSearch();
+  };
+
+  // 鍒濆鍖栨椂闂存Ы锛堜互鍗婂皬鏃朵负闂撮殧锛屼粠8:00鍒�17:30锛�
+  const initTimeSlots = () => {
+    const slots = [];
+    // 鐢熸垚8:00鍒�17:00鐨勬椂闂存
+    for (let hour = 8; hour <= 17; hour++) {
+      // 娣诲姞鏁寸偣
+      slots.push({
+        label: `${hour.toString().padStart(2, "0")}:00`,
+        value: `${hour.toString().padStart(2, "0")}:00`,
+      });
+
+      // 娣诲姞鍗婄偣锛岀洿鍒�17:30
+      if (hour <= 17) {
+        slots.push({
+          label: `${hour.toString().padStart(2, "0")}:30`,
+          value: `${hour.toString().padStart(2, "0")}:30`,
+        });
+      }
+    }
+    // 绉婚櫎鏈�鍚庝竴涓�18:00鐨勬椂闂存
+    if (slots.length > 0 && slots[slots.length - 1].value === "18:00") {
+      slots.pop();
+    }
+    timeSlots.value = slots;
+    console.log(timeSlots.value, "timeSlots.value");
+  };
+
+  // 鐢熸垚浼氳瀹ょ殑鏃堕棿鍗曞厓鏍�
+  const generateMeetingCells = room => {
+    const cells = [];
+    const meetings = room.meetings || [];
+    const occupiedSlots = new Set();
+
+    // 澶勭悊姣忎釜浼氳
+    for (const meeting of meetings) {
+      const startIdx = timeSlots.value.findIndex(
+        slot => slot.value === meeting.startTime
+      );
+      let endIdx = timeSlots.value.findIndex(
+        slot => slot.value === meeting.endTime
+      );
+      if (endIdx === -1) {
+        endIdx = timeSlots.value.length;
+      }
+
+      if (startIdx !== -1) {
+        // 鏍囪琚崰鐢ㄧ殑鏃堕棿娈�
+        for (let i = startIdx; i < endIdx; i++) {
+          if (timeSlots.value[i]) {
+            occupiedSlots.add(timeSlots.value[i].value);
+          }
+        }
+
+        // 鍒涘缓浼氳鍗曞厓鏍�
+        cells.push({
+          type: "meeting",
+          meeting: meeting,
+          span: endIdx - startIdx,
+          startTime: meeting.startTime,
+          endTime: meeting.endTime,
+        });
+      }
+    }
+
+    // 澶勭悊绌洪棽鏃堕棿娈�
+    for (let i = 0; i < timeSlots.value.length; i++) {
+      const slot = timeSlots.value[i];
+      if (!occupiedSlots.has(slot.value)) {
+        // 鏌ユ壘杩炵画鐨勭┖闂叉椂闂存
+        let span = 1;
+        while (
+          i + span < timeSlots.value.length &&
+          !occupiedSlots.has(timeSlots.value[i + span].value)
+        ) {
+          occupiedSlots.add(timeSlots.value[i + span].value);
+          span++;
+        }
+
+        cells.push({
+          type: "free",
+          span: span,
+          time: slot.value,
+        });
+      }
+    }
+
+    // 鎸夋椂闂存帓搴�
+    cells.sort((a, b) => {
+      const timeA = a.startTime || a.time;
+      const timeB = b.startTime || b.time;
+      return (
+        timeSlots.value.findIndex(s => s.value === timeA) -
+        timeSlots.value.findIndex(s => s.value === timeB)
+      );
+    });
+    console.log(cells, "cells");
+    return cells;
+  };
+
+  // 鏌ョ湅浼氳璇︽儏
+  const viewMeetingDetails = cell => {
+    console.log(cell, "cell");
+
+    if (cell && cell.type === "meeting") {
+      currentMeeting.value = cell.meeting;
+      detailDialogVisible.value = true;
+    } else {
+      uni.showToast({
+        title: "璇ユ椂闂存浼氳瀹ょ┖闂�",
+        icon: "info",
+      });
+    }
+  };
+
+  // 鏌ヨ鎸夐挳鎿嶄綔
+  const handleSearch = async () => {
+    loading.value = true;
+    try {
+      const resp = await getMeetingUseList({ ...queryForm });
+      roomUsage.value = resp.data;
+    } catch (error) {
+      uni.showToast({
+        title: "鑾峰彇鏁版嵁澶辫触",
+        icon: "error",
+      });
+    } finally {
+      loading.value = false;
+    }
+  };
+
+  // 閲嶇疆鎼滅储琛ㄥ崟
+  const resetSearch = () => {
+    queryForm.meetingDate = dayjs().format("YYYY-MM-DD");
+  };
+
+  // 椤甸潰鍔犺浇鏃惰幏鍙栨暟鎹�
+  onMounted(() => {
+    // 鍒濆鍖栨椂闂存Ы
+    initTimeSlots();
+
+    // 榛樿鏌ヨ浠婂ぉ鐨勬暟鎹�
+    handleSearch();
+  });
+</script>
+
+<style scoped lang="scss">
+  @import "../../../styles/sales-common.scss";
+  .meeting-list {
+    min-height: 100vh;
+    background: #f8f9fa;
+    padding: 16rpx;
+  }
+
+  .search-section {
+    background: #fff;
+    padding: 16rpx;
+    border-radius: 8rpx;
+    margin-bottom: 16rpx;
+    box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
+  }
+
+  .search-bar {
+    display: flex;
+    align-items: center;
+    gap: 12rpx;
+  }
+
+  .search-input {
+    flex: 1;
+  }
+
+  .filter-button {
+    padding: 12rpx 16rpx;
+    background: #f5f7fa;
+    border-radius: 4rpx;
+  }
+
+  .form-buttons {
+    display: flex;
+    gap: 12rpx;
+    margin-top: 16rpx;
+    justify-content: center;
+  }
+
+  .table-container {
+    margin-top: 16rpx;
+    overflow-x: auto;
+  }
+
+  .time-table {
+    width: 100%;
+  }
+
+  .table-header {
+    display: flex;
+    border: 1rpx solid #e4e7ed;
+    background: #f5f7fa;
+  }
+
+  .header-cell {
+    padding: 12rpx 8rpx;
+    text-align: center;
+    font-weight: bold;
+    border-right: 1rpx solid #e4e7ed;
+  }
+
+  .room-header {
+    width: 120rpx;
+    flex-shrink: 0;
+  }
+
+  .time-header {
+    width: 120rpx;
+    flex-shrink: 0;
+  }
+
+  .table-body {
+    border: 1rpx solid #e4e7ed;
+    border-top: none;
+  }
+
+  .table-row {
+    display: flex;
+    border-top: 1rpx solid #e4e7ed;
+  }
+
+  .table-row:first-child {
+    border-top: none;
+  }
+
+  .cell {
+    padding: 16rpx 8rpx;
+    text-align: center;
+    border-right: 1rpx solid #e4e7ed;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    word-break: break-word;
+    line-height: 1.2;
+  }
+
+  .room-cell {
+    width: 120rpx;
+    font-weight: bold;
+    flex-shrink: 0;
+    background: #f9fafc;
+  }
+
+  .cells-container {
+    display: flex;
+  }
+
+  .content-cell {
+    min-height: 120rpx;
+    cursor: pointer;
+    transition: all 0.3s;
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    padding: 16rpx;
+    box-sizing: border-box;
+  }
+
+  .content-cell:active {
+    opacity: 0.8;
+  }
+
+  .free {
+    color: #f56c6c;
+  }
+
+  .meeting {
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+  }
+
+  .status-1 {
+    background-color: #fef0f0;
+    color: #d14646;
+  }
+
+  .status-0 {
+    background-color: #ecf5ff;
+    color: #409eff;
+  }
+
+  .meeting-content {
+    width: 100%;
+  }
+
+  .meeting-title {
+    font-weight: bold;
+    margin-bottom: 8rpx;
+    font-size: 24rpx;
+  }
+
+  .meeting-time {
+    font-size: 20rpx;
+    opacity: 0.8;
+  }
+
+  .free-content {
+    color: #909399;
+  }
+
+  /* 瀵硅瘽妗嗘牱寮� */
+  .dialog-content {
+    padding: 24rpx;
+  }
+
+  .dialog-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 24rpx;
+    padding-bottom: 16rpx;
+    border-bottom: 1rpx solid #e4e7ed;
+  }
+
+  .dialog-title {
+    font-size: 32rpx;
+    font-weight: bold;
+    color: #303133;
+  }
+
+  .close-icon {
+    font-size: 32rpx;
+    color: #909399;
+  }
+
+  .dialog-body {
+    margin-bottom: 24rpx;
+  }
+
+  .detail-item {
+    display: flex;
+    margin-bottom: 16rpx;
+    padding: 8rpx 0;
+    border-bottom: 1rpx solid #f0f0f0;
+  }
+
+  .detail-label {
+    width: 140rpx;
+    font-weight: bold;
+    color: #606266;
+  }
+
+  .detail-value {
+    flex: 1;
+    color: #303133;
+  }
+
+  .dialog-footer {
+    display: flex;
+    justify-content: center;
+    margin-top: 16rpx;
+  }
+</style>
\ No newline at end of file

--
Gitblit v1.9.3