diff --git a/src/views/calendar/components/addCalendar.vue b/src/views/calendar/components/addCalendar.vue index cf2e0ff..b75f71b 100644 --- a/src/views/calendar/components/addCalendar.vue +++ b/src/views/calendar/components/addCalendar.vue @@ -83,7 +83,7 @@ :value="item.id" >
- {{item.type_detail.name}} | {{ item.name }} + {{item.type_detail?item.type_detail.name:''}} | {{ item.name }} {{ item.is_arrange?'需排课':'无需排课' }}
@@ -516,7 +516,7 @@ export default { : ""; this.form.date = item.start_date; this.form.url = item.url; - this.form.color = item.type_detail.color?item.type_detail.color:'' + this.form.color = item.type_detail?(item.type_detail.color?item.type_detail.color:''):'' } }); } @@ -595,6 +595,7 @@ export default { id: this.id, }).then((res) => { this.form = this.base.requestToForm(res, this.form); + this.form.is_publish = res.is_publish?res.is_publish:0 this.showTinymce = true; }); }, diff --git a/src/views/calendar/index.vue b/src/views/calendar/index.vue index 45bb4ea..22fdd68 100644 --- a/src/views/calendar/index.vue +++ b/src/views/calendar/index.vue @@ -13,10 +13,11 @@
{{ date.getDate() }}
-
@@ -28,8 +29,8 @@
-
{ + 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 传递选项