-
{
+ const events = this.list.filter(ev => {
const startDate = this.parseDateTime(ev.start_time)
// 如果没有end_time,只在start_time的日期显示
@@ -128,6 +129,111 @@ import addCalendar from './components/addCalendar.vue'
const eventStartDate = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate())
return currentDate.getTime() === eventStartDate.getTime()
})
+
+ // 为同一天的事件分配垂直位置,避免重叠
+ return this.arrangeEventsVertically(events)
+ },
+ // 为同一天的事件分配垂直位置,避免重叠
+ arrangeEventsVertically(events) {
+ if (!events || events.length === 0) return events
+
+ // 按开始时间排序
+ const sortedEvents = events.sort((a, b) => {
+ const timeA = this.parseDateTime(a.start_time)
+ const timeB = this.parseDateTime(b.start_time)
+ return timeA.getTime() - timeB.getTime()
+ })
+
+ // 为每个事件分配垂直位置
+ const lanes = [] // 存储每个时间段的占用情况
+ const eventHeight = 16 // 事件高度
+ const eventSpacing = 2 // 事件间距
+
+ sortedEvents.forEach(event => {
+ const startTime = this.parseDateTime(event.start_time)
+ const endTime = event.end_time ? this.parseDateTime(event.end_time) : startTime
+
+ // 计算时间范围(以小时为单位)
+ const startHour = startTime.getHours() + startTime.getMinutes() / 60
+ const endHour = endTime.getHours() + endTime.getMinutes() / 60
+
+ // 找到可用的垂直位置
+ let laneIndex = 0
+ let foundLane = false
+
+ for (let i = 0; i < lanes.length; i++) {
+ const lane = lanes[i]
+ // 检查当前时间段是否与已有事件冲突
+ const hasConflict = lane.some(occupied => {
+ return !(endHour <= occupied.start || startHour >= occupied.end)
+ })
+
+ if (!hasConflict) {
+ laneIndex = i
+ foundLane = true
+ break
+ }
+ }
+
+ // 如果没有找到可用位置,创建新的位置
+ if (!foundLane) {
+ laneIndex = lanes.length
+ lanes.push([])
+ }
+
+ // 记录当前事件占用的时间段
+ lanes[laneIndex].push({
+ start: startHour,
+ end: endHour
+ })
+
+ // 为事件添加垂直位置信息
+ event.verticalPosition = laneIndex
+ // 使用bottom定位时,需要从底部开始计算偏移
+ event.topOffset = (lanes.length - 1 - laneIndex) * (eventHeight + eventSpacing)
+ })
+
+ return sortedEvents
+ },
+ // 获取同一天内所有事件(包括跨天事件)的垂直位置分配
+ getDayEventsWithPositions(date) {
+ const d = new Date(date)
+ const currentDateStr = d.toDateString()
+
+ // 获取单天事件
+ const singleDayEvents = this.list.filter(ev => {
+ const startDate = this.parseDateTime(ev.start_time)
+ const currentDate = new Date(d.getFullYear(), d.getMonth(), d.getDate())
+ const eventStartDate = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate())
+ return currentDate.getTime() === eventStartDate.getTime()
+ })
+
+ // 获取跨天事件(在该日期有显示的事件)
+ const multiDayEvents = this.getContinuousEvents().filter(ev => {
+ const eventStart = new Date(ev.segStartISO)
+ const eventEnd = new Date(ev.segEndISO)
+ return eventStart <= d && d <= eventEnd
+ })
+
+ // 合并所有事件
+ const allEvents = [
+ ...singleDayEvents.map(ev => ({ ...ev, isMultiDay: false })),
+ ...multiDayEvents.map(ev => ({ ...ev, isMultiDay: true }))
+ ]
+
+ // 为所有事件分配垂直位置
+ return this.arrangeEventsVertically(allEvents)
+ },
+ // 获取事件项的样式
+ getEventItemStyle(event) {
+ if (event.topOffset !== undefined) {
+ return {
+ position: 'relative',
+ bottom: `${event.topOffset}px`,
+ zIndex: 70 + (event.verticalPosition || 0) // 确保单天事件在跨天事件之上
+ }
+ }
+ return {}
},
getEventClass(event, date) {
const startDate = new Date(event.start_time)
@@ -205,6 +311,14 @@ import addCalendar from './components/addCalendar.vue'
return s.toDateString() !== e.toDateString()
})
+ // 获取所有单天事件,用于检查冲突
+ const singleDayEvents = this.list.filter(ev => {
+ if (!ev.end_time) return true
+ const s = this.parseDateTime(ev.start_time)
+ const e = this.parseDateTime(ev.end_time)
+ return s.toDateString() === e.toDateString()
+ })
+
const currentMonth = this.calendarDate.getMonth()
const currentYear = this.calendarDate.getFullYear()
const monthStart = new Date(currentYear, currentMonth, 1)
@@ -238,6 +352,15 @@ import addCalendar from './components/addCalendar.vue'
const clampedStart = eventStart < monthStart ? monthStart : eventStart
const clampedEnd = eventEnd > monthEnd ? monthEnd : eventEnd
+ // 检查跨天事件是否与单天事件冲突
+ const hasSingleDayConflict = (date) => {
+ const dateStr = date.toDateString()
+ return singleDayEvents.some(singleEv => {
+ const singleStart = this.parseDateTime(singleEv.start_time)
+ return singleStart.toDateString() === dateStr
+ })
+ }
+
let cursor = getWeekStart(clampedStart)
while (cursor <= clampedEnd) {
const weekStart = new Date(cursor)
@@ -272,7 +395,9 @@ import addCalendar from './components/addCalendar.vue'
displayWeekStartISO: displayWeekStart.toISOString(),
displayStartCol,
displayEndCol,
- laneIndex: 0
+ laneIndex: 0,
+ // 添加冲突信息,用于后续处理
+ hasSingleDayConflict: hasSingleDayConflict
})
}
cursor.setDate(cursor.getDate() + 7)
@@ -360,7 +485,11 @@ import addCalendar from './components/addCalendar.vue'
const dateNumberHeight = 40 // 调整日期数字高度,确保有足够空间
const eventHeight = 16
const eventSpacing = 2
- const verticalOffset = (event.laneIndex || 0) * (eventHeight + eventSpacing)
+
+ // 获取该日期所有事件的垂直位置分配
+ const dayEvents = this.getDayEventsWithPositions(adjStart)
+ const currentEvent = dayEvents.find(ev => ev.id === event.id)
+ const verticalOffset = currentEvent ? currentEvent.topOffset : 0
return {
position: 'absolute',
@@ -368,7 +497,7 @@ import addCalendar from './components/addCalendar.vue'
top: `${headerHeight + weekRow * cellHeight + dateNumberHeight + 25 + verticalOffset}px`, // 增加偏移量到25px,确保课程标题完全在日期数字下方
width: `calc(${event.spanCols * cellWidth}% - 4px)`,
height: `${eventHeight}px`,
- zIndex: 1000,
+ zIndex: 50, // 降低跨天事件的层级
background: `linear-gradient(90deg, ${this.getEventTypeColor(event.type)} 0%, ${this.darkenColor(this.getEventTypeColor(event.type))} 100%)`,
borderRadius: '3px',
fontSize: '11px',
@@ -639,7 +768,7 @@ import addCalendar from './components/addCalendar.vue'
right: 0;
bottom: 0;
pointer-events: none;
- z-index: 100;
+ z-index: 50; /* 降低跨天事件的层级 */
}
.continuous-event {
@@ -650,7 +779,7 @@ import addCalendar from './components/addCalendar.vue'
.continuous-event:hover {
transform: translateY(-1px);
filter: brightness(1.1);
- z-index: 101;
+ z-index: 51; /* 调整悬停时的层级 */
}
/* Element UI 日历样式覆盖 */
@@ -701,7 +830,8 @@ import addCalendar from './components/addCalendar.vue'
flex: 1;
overflow: visible;
position: relative;
- z-index: 5;
+ z-index: 60; /* 提高单天事件的层级,确保在跨天事件之上 */
+ min-height: 60px; /* 确保有足够空间显示多个事件 */
}
.event-item {
@@ -718,6 +848,9 @@ import addCalendar from './components/addCalendar.vue'
text-overflow: ellipsis;
transition: background-color 0.2s;
position: relative;
+ height: 16px; /* 固定高度 */
+ display: flex;
+ align-items: center;
}
.event-title {
diff --git a/vue.config.js b/vue.config.js
index 90cdd01..680480d 100644
--- a/vue.config.js
+++ b/vue.config.js
@@ -27,8 +27,8 @@ module.exports = {
*
*/
publicPath: process.env.ENV === 'staging' ? '/admin' : '/admin',
- outputDir: '/Users/mac/Documents/朗业/2025/s-苏州科技商学院/wx.sstbc.com/public/admin',
- // outputDir: '/Users/mac/Documents/朗业/2024/s-苏州科技商学院/wx.sstbc.com/public/admin',
+ // outputDir: '/Users/mac/Documents/朗业/2025/s-苏州科技商学院/wx.sstbc.com/public/admin',
+ outputDir: '/Users/mac/Documents/朗业/2024/s-苏州科技商学院/wx.sstbc.com/public/admin',
assetsDir: 'static',
css: {
loaderOptions: { // 向 CSS 相关的 loader 传递选项