📌 背景介绍
最近在做一个会议室预约系统,需要实现一个「日历时间轴」页面:
- 上方显示日期(支持左右滑动切换)
- 左侧是时间轴(例如 08:30、09:00、09:30 …)
- 右侧展示会议预约块(不同时间段显示会议内容)
- 支持动态计算高度、重叠会议的布局、当前时间线显示等。
🧩 实现步骤
- 先定义左侧固定的时间刻度
左侧显示固定的时间刻度
const timeMap = {
1: '08:30',
2: '09:00',
3: '09:30',
4: '10:00',
5: '10:30',
// ...
};
- 会议预约内容
时间使用具体的数字和timeMap进行映射
{
id: 1,
startTime: 2,
endTime: 6,
data: {
title: '项目讨论',
host: '张三',
comment: '主要讨论开发进度',
}
}
- 动态计算每个会议块的位置以及高度
const SLOT_HEIGHT = 100 // 每半小时的高度
const SLOT_LEFT = 72 // 左侧偏移的部分
const OVERLAP_OFFSET = 70 // 如果有多个的话会有重叠部分
// 动态计算每个会议块的位置
const positionedMeetings = computed(() => {
if (!data.value?.data) return []
const result: any[] = []
const filteredMeetings = data.value.data.filter((m) => {
return m.meetingDate === selectedDate.value
})
filteredMeetings.forEach((m) => {
if (!m.startTime) m.startTime = 1
if (!m.endTime || m.endTime <= m.startTime) m.endTime = m.startTime + 1
if (m.startTime % 2 !== 0) return
const top = (m.startTime - 1) * SLOT_HEIGHT
const height = Math.max((m.endTime - m.startTime) * SLOT_HEIGHT, SLOT_HEIGHT)
// 找出重叠会议块
const overlapping = result.filter(
(r) => !(r.endTime <= m.startTime || r.startTime >= m.endTime),
)
const left = SLOT_LEFT + overlapping.length * OVERLAP_OFFSET
result.push({
id: m.id,
startTime: m.startTime,
endTime: m.endTime,
conferenceStatus: m.conferenceStatus,
data: m,
top,
height,
left,
})
})
return result
})
- 当同一时间段存在多个会议室的预约时,多个会议块会在页面上重叠。为提升交互体验,在用户点击会议块时,会动态调整该会议块的 z-index,让它显示在最上方,避免被其他会议信息遮挡。
// 子组件请求提升层级时调用
function raiseLayer() {
popupZIndex.value = 9999
// 也可以同步调整会议块层级,比如:
positionedMeetings.value.forEach((m) => (m.zIndex = 1))
}
// 子组件请求恢复层级
function restoreLayer() {
popupZIndex.value = 2000
}
//点击会议卡片的时候切换层级
function viewMeetingDetails(meeting) {
meetingDetailsShow.value = true
currentMeeting.value = { ...meeting } // 拷贝为普通对象
// 计算当前的最大卡片 zIndex
const all = positionedMeetings.value
const maxZ = Math.max(...all.map((m) => m.zIndex ?? 10))
// 提升被点击卡片到最上
meeting.zIndex = maxZ + 1
// 为了防止 zIndex 不断变大,我们可以做一次归整(可选)
// 按 zIndex 排序并重新分配基准 zIndex,使值保持在一个合理范围
const sorted = all.slice().sort((a, b) => (b.zIndex ?? 10) - (a.zIndex ?? 10))
let base = 100 // 你可以设置基值
sorted.forEach((m, i) => {
m.zIndex = base - i // 大的在上,小的在下
})
// uni.navigateTo({ url: '/pages-sub/workbench/chamber/meetingDetails' })
}
function handleMeetingDetailsClose(meeting) {
meetingDetailsShow.value = false
positionedMeetings.value.forEach((m) => {
m.zIndex = m.initialZIndex
})
}
以上是部分关键代码,完整代码在