|
|
|
@ -56,19 +56,80 @@ |
|
|
|
|
|
|
|
<!-- 课程表主体 --> |
|
|
|
<view class="schedule-main"> |
|
|
|
<!-- 左侧固定列 --> |
|
|
|
<view class="time-column-fixed"> |
|
|
|
<!-- 左上角标题 --> |
|
|
|
<view class="time-header-cell"> |
|
|
|
<template v-if="activeFilter === 'time' || activeFilter === ''">时间</template> |
|
|
|
<template v-else-if="activeFilter === 'teacher'">教练</template> |
|
|
|
<template v-else-if="activeFilter === 'classroom'">教室</template> |
|
|
|
<template v-else-if="activeFilter === 'class'">班级</template> |
|
|
|
</view> |
|
|
|
|
|
|
|
<!-- 左侧列内容 --> |
|
|
|
<view class="time-rows-container"> |
|
|
|
<!-- 时间模式 --> |
|
|
|
<template v-if="activeFilter === 'time' || activeFilter === ''"> |
|
|
|
<view |
|
|
|
class="time-cell" |
|
|
|
v-for="(timeSlot, timeIndex) in timeSlots" |
|
|
|
:key="timeIndex" |
|
|
|
:class="[!timeSlot.available ? 'time-unavailable' : '']" |
|
|
|
> |
|
|
|
{{ timeSlot.time }} |
|
|
|
</view> |
|
|
|
</template> |
|
|
|
|
|
|
|
<!-- 教练模式 --> |
|
|
|
<template v-else-if="activeFilter === 'teacher'"> |
|
|
|
<view |
|
|
|
class="time-cell" |
|
|
|
v-for="(teacher, index) in teacherOptions" |
|
|
|
:key="teacher.id" |
|
|
|
> |
|
|
|
{{ teacher.name }} |
|
|
|
</view> |
|
|
|
</template> |
|
|
|
|
|
|
|
<!-- 教室模式 --> |
|
|
|
<template v-else-if="activeFilter === 'classroom'"> |
|
|
|
<view |
|
|
|
class="time-cell" |
|
|
|
v-for="(venue, index) in venues" |
|
|
|
:key="venue.id" |
|
|
|
> |
|
|
|
{{ venue.name }} |
|
|
|
</view> |
|
|
|
</template> |
|
|
|
|
|
|
|
<!-- 班级模式 --> |
|
|
|
<template v-else-if="activeFilter === 'class'"> |
|
|
|
<view |
|
|
|
class="time-cell" |
|
|
|
v-for="(cls, index) in classOptions" |
|
|
|
:key="cls.id" |
|
|
|
> |
|
|
|
{{ cls.name }} |
|
|
|
</view> |
|
|
|
</template> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
|
|
|
|
<!-- 右侧可滚动内容区 --> |
|
|
|
<view class="schedule-scrollable-area"> |
|
|
|
<!-- 水平滚动容器 --> |
|
|
|
<scroll-view |
|
|
|
class="schedule-scroll-horizontal" |
|
|
|
scroll-x |
|
|
|
scroll-y |
|
|
|
:scroll-left="scrollLeft" |
|
|
|
@scroll="onHorizontalScroll" |
|
|
|
:scroll-top="scrollTop" |
|
|
|
@scroll="onScroll" |
|
|
|
:style="{ width: '100%' }" |
|
|
|
> |
|
|
|
<view class="schedule-table" :style="{ width: tableWidth + 'rpx' }"> |
|
|
|
<view class="scroll-content" :style="{ width: tableWidth + 'rpx', minWidth: '1400rpx' }"> |
|
|
|
<!-- 表头 - 日期行 --> |
|
|
|
<view class="table-header"> |
|
|
|
<!-- 左上角时间标题 --> |
|
|
|
<view class="time-header-cell">时间</view> |
|
|
|
|
|
|
|
<!-- 日期列 --> |
|
|
|
<view |
|
|
|
class="date-header-cell" |
|
|
|
@ -81,36 +142,61 @@ |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
|
|
|
|
<!-- 表格内容 - 垂直滚动 --> |
|
|
|
<scroll-view |
|
|
|
class="schedule-scroll-vertical" |
|
|
|
scroll-y |
|
|
|
:scroll-top="scrollTop" |
|
|
|
@scroll="onVerticalScroll" |
|
|
|
> |
|
|
|
<view class="table-body"> |
|
|
|
<!-- 课程内容区 --> |
|
|
|
<view class="course-content-area"> |
|
|
|
<!-- 时间模式内容 --> |
|
|
|
<template v-if="activeFilter === 'time' || activeFilter === ''"> |
|
|
|
<view |
|
|
|
class="time-row" |
|
|
|
v-for="(timeSlot, timeIndex) in timeSlots" |
|
|
|
:key="timeIndex" |
|
|
|
> |
|
|
|
<!-- 时间列 --> |
|
|
|
<!-- 课程列 --> |
|
|
|
<view |
|
|
|
:class="['time-cell', !timeSlot.available ? 'time-unavailable' : '']" |
|
|
|
:class="['course-cell',!timeSlot.available ? 'cell-unavailable' : '']" |
|
|
|
v-for="(date, dateIndex) in weekDates" |
|
|
|
:key="dateIndex" |
|
|
|
@click="timeSlot.available ? handleCellClick(timeSlot, date) : null" |
|
|
|
> |
|
|
|
{{ timeSlot.time }} |
|
|
|
<!-- 课程项目 --> |
|
|
|
<view |
|
|
|
class="course-item" |
|
|
|
v-for="course in getCoursesByTimeAndDate(timeSlot.time, date.date)" |
|
|
|
:key="course.id" |
|
|
|
:class="[ |
|
|
|
course.type === 'normal' ? 'course-normal' : '', |
|
|
|
course.type === 'private' ? 'course-private' : '', |
|
|
|
course.type === 'activity' ? 'course-activity' : '' |
|
|
|
]" |
|
|
|
@click.stop="viewScheduleDetail(course.id)" |
|
|
|
> |
|
|
|
<view class="course-name">{{ course.courseName }}</view> |
|
|
|
<view class="course-students">{{ course.students }}</view> |
|
|
|
<view class="course-teacher">{{ course.teacher }}</view> |
|
|
|
<view class="course-status">{{ course.status }}</view> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
</template> |
|
|
|
|
|
|
|
<!-- 教练模式内容 --> |
|
|
|
<template v-else-if="activeFilter === 'teacher'"> |
|
|
|
<view |
|
|
|
class="time-row" |
|
|
|
v-for="(teacher, teacherIndex) in teacherOptions" |
|
|
|
:key="teacher.id" |
|
|
|
> |
|
|
|
<!-- 课程列 --> |
|
|
|
<view |
|
|
|
:class="['course-cell',!timeSlot.available ? 'cell-unavailable' : '']" |
|
|
|
class="course-cell" |
|
|
|
v-for="(date, dateIndex) in weekDates" |
|
|
|
:key="dateIndex" |
|
|
|
@click="timeSlot.available ? handleCellClick(timeSlot, date) : null" |
|
|
|
@click="handleCellClick({time: ''}, date, teacher.id)" |
|
|
|
> |
|
|
|
<!-- 课程项目 --> |
|
|
|
<view |
|
|
|
class="course-item" |
|
|
|
v-for="course in getCoursesByTimeAndDate(timeSlot.time, date.date)" |
|
|
|
v-for="course in getCoursesByTeacherAndDate(teacher.id, date.date)" |
|
|
|
:key="course.id" |
|
|
|
:class="[ |
|
|
|
course.type === 'normal' ? 'course-normal' : '', |
|
|
|
@ -120,17 +206,88 @@ |
|
|
|
@click.stop="viewScheduleDetail(course.id)" |
|
|
|
> |
|
|
|
<view class="course-name">{{ course.courseName }}</view> |
|
|
|
<view class="course-time">{{ course.time }}</view> |
|
|
|
<view class="course-students">{{ course.students }}</view> |
|
|
|
<view class="course-status">{{ course.status }}</view> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
</template> |
|
|
|
|
|
|
|
<!-- 教室模式内容 --> |
|
|
|
<template v-else-if="activeFilter === 'classroom'"> |
|
|
|
<view |
|
|
|
class="time-row" |
|
|
|
v-for="(venue, venueIndex) in venues" |
|
|
|
:key="venue.id" |
|
|
|
> |
|
|
|
<!-- 课程列 --> |
|
|
|
<view |
|
|
|
class="course-cell" |
|
|
|
v-for="(date, dateIndex) in weekDates" |
|
|
|
:key="dateIndex" |
|
|
|
@click="handleCellClick({time: ''}, date, null, venue.id)" |
|
|
|
> |
|
|
|
<!-- 课程项目 --> |
|
|
|
<view |
|
|
|
class="course-item" |
|
|
|
v-for="course in getCoursesByVenueAndDate(venue.id, date.date)" |
|
|
|
:key="course.id" |
|
|
|
:class="[ |
|
|
|
course.type === 'normal' ? 'course-normal' : '', |
|
|
|
course.type === 'private' ? 'course-private' : '', |
|
|
|
course.type === 'activity' ? 'course-activity' : '' |
|
|
|
]" |
|
|
|
@click.stop="viewScheduleDetail(course.id)" |
|
|
|
> |
|
|
|
<view class="course-name">{{ course.courseName }}</view> |
|
|
|
<view class="course-time">{{ course.time }}</view> |
|
|
|
<view class="course-teacher">{{ course.teacher }}</view> |
|
|
|
<view class="course-status">{{ course.status }}</view> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
</template> |
|
|
|
|
|
|
|
<!-- 班级模式内容 --> |
|
|
|
<template v-else-if="activeFilter === 'class'"> |
|
|
|
<view |
|
|
|
class="time-row" |
|
|
|
v-for="(cls, clsIndex) in classOptions" |
|
|
|
:key="cls.id" |
|
|
|
> |
|
|
|
<!-- 课程列 --> |
|
|
|
<view |
|
|
|
class="course-cell" |
|
|
|
v-for="(date, dateIndex) in weekDates" |
|
|
|
:key="dateIndex" |
|
|
|
@click="handleCellClick({time: ''}, date, null, null, cls.id)" |
|
|
|
> |
|
|
|
<!-- 课程项目 --> |
|
|
|
<view |
|
|
|
class="course-item" |
|
|
|
v-for="course in getCoursesByClassAndDate(cls.id, date.date)" |
|
|
|
:key="course.id" |
|
|
|
:class="[ |
|
|
|
course.type === 'normal' ? 'course-normal' : '', |
|
|
|
course.type === 'private' ? 'course-private' : '', |
|
|
|
course.type === 'activity' ? 'course-activity' : '' |
|
|
|
]" |
|
|
|
@click.stop="viewScheduleDetail(course.id)" |
|
|
|
> |
|
|
|
<view class="course-name">{{ course.courseName }}</view> |
|
|
|
<view class="course-time">{{ course.time }}</view> |
|
|
|
<view class="course-teacher">{{ course.teacher }}</view> |
|
|
|
<view class="course-status">{{ course.status }}</view> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
</template> |
|
|
|
</view> |
|
|
|
</scroll-view> |
|
|
|
</view> |
|
|
|
</scroll-view> |
|
|
|
</view> |
|
|
|
</view> |
|
|
|
|
|
|
|
<!-- 添加按钮 --> |
|
|
|
<view class="add-btn" @click="addCourse"> |
|
|
|
@ -266,9 +423,10 @@ export default { |
|
|
|
// 滚动相关 |
|
|
|
scrollLeft: 0, |
|
|
|
scrollTop: 0, |
|
|
|
scrollAnimationFrame: null, // 滚动动画帧 |
|
|
|
|
|
|
|
// 表格配置 |
|
|
|
tableWidth: 1200, // 表格总宽度 |
|
|
|
tableWidth: 1500, // 表格总宽度,确保7天都能显示 (7*180+120=1380rpx) |
|
|
|
|
|
|
|
// 时间段配置(动态生成,支持场地时间限制) |
|
|
|
timeSlots: [], |
|
|
|
@ -353,6 +511,22 @@ export default { |
|
|
|
this.initTimeSlots() |
|
|
|
this.loadFilterOptions() |
|
|
|
this.loadScheduleList() |
|
|
|
|
|
|
|
// 先初始化模拟数据 |
|
|
|
this.initMockData() |
|
|
|
|
|
|
|
// 监听窗口大小变化,调整布局 |
|
|
|
window.addEventListener('resize', this.handleResize) |
|
|
|
}, |
|
|
|
|
|
|
|
beforeDestroy() { |
|
|
|
// 移除事件监听 |
|
|
|
window.removeEventListener('resize', this.handleResize) |
|
|
|
|
|
|
|
// 清除任何未完成的动画帧 |
|
|
|
if (this.scrollAnimationFrame) { |
|
|
|
cancelAnimationFrame(this.scrollAnimationFrame) |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
methods: { |
|
|
|
@ -360,8 +534,15 @@ export default { |
|
|
|
initCurrentWeek() { |
|
|
|
const today = new Date() |
|
|
|
const currentDay = today.getDay() // 0是周日,1是周一 |
|
|
|
const diff = today.getDate() - currentDay + (currentDay === 0 ? -6 : 1) // 调整到周一 |
|
|
|
const monday = new Date(today.setDate(diff)) |
|
|
|
|
|
|
|
// 计算本周周一的日期 |
|
|
|
let diff = 1 - currentDay // 距离周一的天数 |
|
|
|
if (currentDay === 0) { // 如果今天是周日,则向前调7天到上周一 |
|
|
|
diff = -6 |
|
|
|
} |
|
|
|
|
|
|
|
const monday = new Date(today) |
|
|
|
monday.setDate(today.getDate() + diff) |
|
|
|
this.currentWeekStart = monday.toISOString().split('T')[0] |
|
|
|
|
|
|
|
// 设置筛选参数的日期范围 |
|
|
|
@ -513,13 +694,61 @@ export default { |
|
|
|
// 获取指定时间和日期的课程 |
|
|
|
getCoursesByTimeAndDate(time, date) { |
|
|
|
return this.courses.filter(course => |
|
|
|
course.time === time && course.date === date, |
|
|
|
course.time === time && course.date === date |
|
|
|
) |
|
|
|
}, |
|
|
|
|
|
|
|
// 获取指定教练和日期的课程 |
|
|
|
getCoursesByTeacherAndDate(teacherId, date) { |
|
|
|
return this.courses.filter(course => { |
|
|
|
// 匹配教练ID |
|
|
|
const teacherMatch = course.teacher_id === teacherId || |
|
|
|
(this.teacherOptions.find(t => t.name === course.teacher)?.id === teacherId); |
|
|
|
// 匹配日期 |
|
|
|
return teacherMatch && course.date === date; |
|
|
|
}); |
|
|
|
}, |
|
|
|
|
|
|
|
// 获取指定教室和日期的课程 |
|
|
|
getCoursesByVenueAndDate(venueId, date) { |
|
|
|
return this.courses.filter(course => { |
|
|
|
// 匹配教室ID |
|
|
|
const venueMatch = course.venue_id === venueId || |
|
|
|
(this.venues.find(v => v.name === course.venue)?.id === venueId); |
|
|
|
// 匹配日期 |
|
|
|
return venueMatch && course.date === date; |
|
|
|
}); |
|
|
|
}, |
|
|
|
|
|
|
|
// 获取指定班级和日期的课程 |
|
|
|
getCoursesByClassAndDate(classId, date) { |
|
|
|
return this.courses.filter(course => { |
|
|
|
// 匹配班级ID |
|
|
|
const classMatch = course.class_id === classId || |
|
|
|
(this.classOptions.find(c => course.courseName.includes(c.name))?.id === classId); |
|
|
|
// 匹配日期 |
|
|
|
return classMatch && course.date === date; |
|
|
|
}); |
|
|
|
}, |
|
|
|
|
|
|
|
// 打开筛选 |
|
|
|
openFilter(type) { |
|
|
|
this.activeFilter = this.activeFilter === type ? '' : type |
|
|
|
// 如果已经是当前类型,则取消选中,否则切换为新类型 |
|
|
|
const newFilter = this.activeFilter === type ? '' : type; |
|
|
|
|
|
|
|
// 只有当筛选类型发生变化时才执行操作 |
|
|
|
if (this.activeFilter !== newFilter) { |
|
|
|
this.activeFilter = newFilter; |
|
|
|
|
|
|
|
// 如果切换了模式,重置滚动位置 |
|
|
|
this.scrollLeft = 0; |
|
|
|
this.scrollTop = 0; |
|
|
|
|
|
|
|
// 清除任何未完成的动画帧 |
|
|
|
if (this.scrollAnimationFrame) { |
|
|
|
cancelAnimationFrame(this.scrollAnimationFrame); |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
// 关闭筛选弹窗 |
|
|
|
@ -528,7 +757,7 @@ export default { |
|
|
|
}, |
|
|
|
|
|
|
|
// 筛选确认 |
|
|
|
handleFilterConfirm(e) { |
|
|
|
async handleFilterConfirm(e) { |
|
|
|
if (e.index === 0) { |
|
|
|
// 重置 |
|
|
|
this.resetFilters() |
|
|
|
@ -536,6 +765,16 @@ export default { |
|
|
|
// 确定 |
|
|
|
this.applyFilters() |
|
|
|
this.closeFilterModal() |
|
|
|
|
|
|
|
// 重新加载数据后,重置滚动位置 |
|
|
|
await this.loadScheduleList() |
|
|
|
this.scrollLeft = 0 |
|
|
|
this.scrollTop = 0 |
|
|
|
|
|
|
|
// 如果筛选改变了时间段,需要重新生成时间列 |
|
|
|
if (this.selectedTimeRange !== '' || this.selectedVenueId !== null) { |
|
|
|
this.initTimeSlots() |
|
|
|
} |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
@ -623,7 +862,28 @@ export default { |
|
|
|
// 加载筛选选项 |
|
|
|
async loadFilterOptions() { |
|
|
|
try { |
|
|
|
const res = await api.getCourseScheduleFilterOptions() |
|
|
|
// 直接使用已初始化的模拟数据 |
|
|
|
// 注释掉实际 API 调用,之后再实现 |
|
|
|
// const res = await api.getCourseScheduleFilterOptions() |
|
|
|
|
|
|
|
const res = { |
|
|
|
code: 1, |
|
|
|
data: { |
|
|
|
coaches: this.teacherOptions, |
|
|
|
venues: this.venues.map(venue => ({ |
|
|
|
id: venue.id, |
|
|
|
venue_name: venue.name, |
|
|
|
capacity: venue.capacity, |
|
|
|
description: venue.description || '' |
|
|
|
})), |
|
|
|
classes: this.classOptions.map(cls => ({ |
|
|
|
id: cls.id, |
|
|
|
class_name: cls.name, |
|
|
|
class_level: cls.level, |
|
|
|
total_students: cls.students |
|
|
|
})) |
|
|
|
} |
|
|
|
} |
|
|
|
if (res.code === 1) { |
|
|
|
const data = res.data |
|
|
|
|
|
|
|
@ -669,7 +929,8 @@ export default { |
|
|
|
try { |
|
|
|
this.loading = true |
|
|
|
|
|
|
|
const res = await api.getCourseScheduleList(this.filterParams) |
|
|
|
// 使用模拟数据 API |
|
|
|
const res = await api.getCourseScheduleListMock(this.filterParams) |
|
|
|
|
|
|
|
if (res.code === 1) { |
|
|
|
// 转换数据格式 |
|
|
|
@ -716,22 +977,56 @@ export default { |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
// 水平滚动事件 |
|
|
|
onHorizontalScroll(e) { |
|
|
|
// 滚动事件处理函数 |
|
|
|
onScroll(e) { |
|
|
|
if (this.scrollAnimationFrame) { |
|
|
|
cancelAnimationFrame(this.scrollAnimationFrame) |
|
|
|
} |
|
|
|
|
|
|
|
this.scrollAnimationFrame = requestAnimationFrame(() => { |
|
|
|
// 获取滚动位置 |
|
|
|
if (e.detail.scrollLeft !== undefined) { |
|
|
|
this.scrollLeft = e.detail.scrollLeft |
|
|
|
}, |
|
|
|
} |
|
|
|
|
|
|
|
// 垂直滚动事件 |
|
|
|
onVerticalScroll(e) { |
|
|
|
if (e.detail.scrollTop !== undefined) { |
|
|
|
this.scrollTop = e.detail.scrollTop |
|
|
|
|
|
|
|
// 更新左侧时间列的滚动位置 |
|
|
|
this.$nextTick(() => { |
|
|
|
const timeRowsContainer = this.$el.querySelector('.time-rows-container') |
|
|
|
if (timeRowsContainer) { |
|
|
|
timeRowsContainer.scrollTop = this.scrollTop |
|
|
|
} |
|
|
|
}) |
|
|
|
} |
|
|
|
}) |
|
|
|
}, |
|
|
|
|
|
|
|
// 单元格点击 |
|
|
|
handleCellClick(timeSlot, date) { |
|
|
|
// 打开添加课程课程安排页面,并传递时间和日期信息 |
|
|
|
uni.navigateTo({ |
|
|
|
url: `/pages/coach/schedule/add_schedule?date=${date.date}&time=${timeSlot.time}&time_slot=${timeSlot.timeSlot}`, |
|
|
|
}) |
|
|
|
handleCellClick(timeSlot, date, teacherId = null, venueId = null, classId = null) { |
|
|
|
// 构建跳转 URL |
|
|
|
let url = `/pages/coach/schedule/add_schedule?date=${date.date}`; |
|
|
|
|
|
|
|
// 根据不同模式添加参数 |
|
|
|
if (timeSlot && timeSlot.time) { |
|
|
|
url += `&time=${timeSlot.time}&time_slot=${timeSlot.timeSlot || ''}`; |
|
|
|
} |
|
|
|
|
|
|
|
if (teacherId) { |
|
|
|
url += `&coach_id=${teacherId}`; |
|
|
|
} |
|
|
|
|
|
|
|
if (venueId) { |
|
|
|
url += `&venue_id=${venueId}`; |
|
|
|
} |
|
|
|
|
|
|
|
if (classId) { |
|
|
|
url += `&class_id=${classId}`; |
|
|
|
} |
|
|
|
|
|
|
|
// 打开添加课程安排页面 |
|
|
|
uni.navigateTo({ url }); |
|
|
|
}, |
|
|
|
|
|
|
|
// 添加课程 |
|
|
|
@ -762,6 +1057,49 @@ export default { |
|
|
|
url: `/pages/coach/schedule/adjust_course?id=${data.scheduleId}`, |
|
|
|
}) |
|
|
|
}, |
|
|
|
|
|
|
|
// 响应式调整 |
|
|
|
handleResize() { |
|
|
|
// 根据窗口宽度调整表格尺寸 |
|
|
|
const width = window.innerWidth |
|
|
|
if (width <= 375) { |
|
|
|
this.tableWidth = 1220 // 7*160+100=1220rpx |
|
|
|
} else if (width <= 768) { |
|
|
|
this.tableWidth = 1400 |
|
|
|
} else { |
|
|
|
this.tableWidth = 1500 |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
// 初始化模拟数据 |
|
|
|
initMockData() { |
|
|
|
// 模拟教练选项 |
|
|
|
this.teacherOptions = [ |
|
|
|
{ id: 1, name: '张教练', avatar: '/static/avatar/teacher1.png' }, |
|
|
|
{ id: 2, name: '李教练', avatar: '/static/avatar/teacher2.png' }, |
|
|
|
{ id: 3, name: '王教练', avatar: '/static/avatar/teacher3.png' }, |
|
|
|
{ id: 4, name: '刘教练', avatar: '/static/avatar/teacher4.png' }, |
|
|
|
{ id: 5, name: '赵教练', avatar: '/static/avatar/teacher5.png' } |
|
|
|
]; |
|
|
|
|
|
|
|
// 模拟场地选项 |
|
|
|
this.venues = [ |
|
|
|
{ id: 1, name: '舞蹈室A', capacity: 20, time_range_type: 'range', time_range_start: '09:00', time_range_end: '21:00' }, |
|
|
|
{ id: 2, name: '瑜伽B', capacity: 15, time_range_type: 'range', time_range_start: '08:00', time_range_end: '20:00' }, |
|
|
|
{ id: 3, name: '健身C', capacity: 10, time_range_type: 'all' }, |
|
|
|
{ id: 4, name: '泳池D', capacity: 8, time_range_type: 'fixed', fixed_time_ranges: ['09:00-11:00', '14:00-18:00'] }, |
|
|
|
{ id: 5, name: '综合场馆E', capacity: 30, time_range_type: 'range', time_range_start: '08:00', time_range_end: '22:00' } |
|
|
|
]; |
|
|
|
|
|
|
|
// 模拟班级选项 |
|
|
|
this.classOptions = [ |
|
|
|
{ id: 1, name: '少儿形体班', level: '初级', students: 15 }, |
|
|
|
{ id: 2, name: '成人瑜伽班', level: '中级', students: 20 }, |
|
|
|
{ id: 3, name: '私教VIP班', level: '高级', students: 5 }, |
|
|
|
{ id: 4, name: '儿童游泳班', level: '初级', students: 12 }, |
|
|
|
{ id: 5, name: '暑期特训班', level: '混合', students: 25 } |
|
|
|
]; |
|
|
|
}, |
|
|
|
}, |
|
|
|
} |
|
|
|
</script> |
|
|
|
@ -844,6 +1182,68 @@ export default { |
|
|
|
|
|
|
|
// 课程表主体 |
|
|
|
.schedule-main { |
|
|
|
flex: 1; |
|
|
|
position: relative; |
|
|
|
display: flex; |
|
|
|
overflow: hidden; |
|
|
|
} |
|
|
|
|
|
|
|
// 左侧固定时间列 |
|
|
|
.time-column-fixed { |
|
|
|
width: 120rpx; |
|
|
|
position: relative; |
|
|
|
z-index: 2; |
|
|
|
background-color: #292929; |
|
|
|
display: flex; |
|
|
|
flex-direction: column; |
|
|
|
box-shadow: 4rpx 0 8rpx rgba(0, 0, 0, 0.1); |
|
|
|
} |
|
|
|
|
|
|
|
.time-header-cell { |
|
|
|
width: 120rpx; |
|
|
|
height: 120rpx; // 需与表头高度一致 |
|
|
|
padding: 20rpx 10rpx; |
|
|
|
color: #29d3b4; |
|
|
|
font-size: 28rpx; |
|
|
|
font-weight: 500; |
|
|
|
text-align: center; |
|
|
|
border-right: 1px solid #555; |
|
|
|
background-color: #434544; |
|
|
|
border-bottom: 2px solid #29d3b4; |
|
|
|
} |
|
|
|
|
|
|
|
.time-rows-container { |
|
|
|
flex: 1; |
|
|
|
display: flex; |
|
|
|
flex-direction: column; |
|
|
|
overflow-y: hidden; |
|
|
|
min-height: 1000rpx; /* 确保有足够的高度能滚动 */ |
|
|
|
} |
|
|
|
|
|
|
|
.time-cell { |
|
|
|
width: 120rpx; |
|
|
|
height: 120rpx; /* 固定高度保证对齐 */ |
|
|
|
min-height: 120rpx; |
|
|
|
padding: 20rpx 10rpx; |
|
|
|
color: #999; |
|
|
|
font-size: 24rpx; |
|
|
|
text-align: center; |
|
|
|
border-right: 1px solid #434544; |
|
|
|
border-bottom: 1px solid #434544; |
|
|
|
background: #3a3a3a; |
|
|
|
display: flex; |
|
|
|
align-items: center; |
|
|
|
justify-content: center; |
|
|
|
|
|
|
|
&.time-unavailable { |
|
|
|
background: #2a2a2a; |
|
|
|
color: #555; |
|
|
|
opacity: 0.5; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 右侧可滚动区域 |
|
|
|
.schedule-scrollable-area { |
|
|
|
flex: 1; |
|
|
|
position: relative; |
|
|
|
overflow: hidden; |
|
|
|
@ -852,44 +1252,35 @@ export default { |
|
|
|
.schedule-scroll-horizontal { |
|
|
|
width: 100%; |
|
|
|
height: 100%; |
|
|
|
overflow: scroll; |
|
|
|
} |
|
|
|
|
|
|
|
.schedule-table { |
|
|
|
min-width: 100%; |
|
|
|
height: 100%; |
|
|
|
.scroll-content { |
|
|
|
display: flex; |
|
|
|
flex-direction: column; |
|
|
|
min-width: 1500rpx; |
|
|
|
} |
|
|
|
|
|
|
|
// 表头 |
|
|
|
.table-header { |
|
|
|
display: flex; |
|
|
|
background: #434544; |
|
|
|
border-bottom: 2px solid #29d3b4; |
|
|
|
position: sticky; |
|
|
|
top: 0; |
|
|
|
z-index: 10; |
|
|
|
} |
|
|
|
|
|
|
|
.time-header-cell { |
|
|
|
width: 120rpx; |
|
|
|
min-width: 120rpx; |
|
|
|
padding: 20rpx 10rpx; |
|
|
|
color: #29d3b4; |
|
|
|
font-size: 28rpx; |
|
|
|
font-weight: 500; |
|
|
|
text-align: center; |
|
|
|
border-right: 1px solid #555; |
|
|
|
z-index: 1; |
|
|
|
min-width: 1260rpx; /* 7天 * 180rpx = 1260rpx */ |
|
|
|
} |
|
|
|
|
|
|
|
.date-header-cell { |
|
|
|
width: 150rpx; |
|
|
|
min-width: 150rpx; |
|
|
|
width: 180rpx; |
|
|
|
min-width: 180rpx; |
|
|
|
padding: 15rpx 10rpx; |
|
|
|
color: #fff; |
|
|
|
font-size: 24rpx; |
|
|
|
text-align: center; |
|
|
|
border-right: 1px solid #555; |
|
|
|
background: #434544; |
|
|
|
flex-shrink: 0; |
|
|
|
|
|
|
|
.date-week { |
|
|
|
font-size: 26rpx; |
|
|
|
@ -909,45 +1300,28 @@ export default { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 表格内容 |
|
|
|
.schedule-scroll-vertical { |
|
|
|
flex: 1; |
|
|
|
height: 100%; |
|
|
|
} |
|
|
|
|
|
|
|
.table-body { |
|
|
|
.course-content-area { |
|
|
|
width: 100%; |
|
|
|
min-height: 1000rpx; /* 确保有足够的高度能滚动 */ |
|
|
|
min-width: 1260rpx; /* 7天 * 180rpx = 1260rpx */ |
|
|
|
} |
|
|
|
|
|
|
|
.time-row { |
|
|
|
display: flex; |
|
|
|
height: 120rpx; /* 固定高度保证对齐 */ |
|
|
|
min-height: 120rpx; |
|
|
|
border-bottom: 1px solid #434544; |
|
|
|
} |
|
|
|
|
|
|
|
.time-cell { |
|
|
|
width: 120rpx; |
|
|
|
min-width: 120rpx; |
|
|
|
padding: 20rpx 10rpx; |
|
|
|
color: #999; |
|
|
|
font-size: 24rpx; |
|
|
|
text-align: center; |
|
|
|
border-right: 1px solid #434544; |
|
|
|
background: #3a3a3a; |
|
|
|
|
|
|
|
&.time-unavailable { |
|
|
|
background: #2a2a2a; |
|
|
|
color: #555; |
|
|
|
opacity: 0.5; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
.course-cell { |
|
|
|
width: 150rpx; |
|
|
|
min-width: 150rpx; |
|
|
|
width: 180rpx; |
|
|
|
min-width: 180rpx; |
|
|
|
padding: 10rpx; |
|
|
|
border-right: 1px solid #434544; |
|
|
|
border-bottom: 1px solid #434544; |
|
|
|
position: relative; |
|
|
|
background: #292929; |
|
|
|
flex-shrink: 0; |
|
|
|
|
|
|
|
&.cell-unavailable { |
|
|
|
background: #1e1e1e; |
|
|
|
@ -967,6 +1341,28 @@ export default { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 响应式适配 |
|
|
|
@media screen and (max-width: 375px) { |
|
|
|
.time-column-fixed { |
|
|
|
width: 100rpx; |
|
|
|
} |
|
|
|
|
|
|
|
.time-header-cell, .time-cell { |
|
|
|
width: 100rpx; |
|
|
|
font-size: 22rpx; |
|
|
|
} |
|
|
|
|
|
|
|
.course-cell { |
|
|
|
width: 160rpx; |
|
|
|
min-width: 160rpx; |
|
|
|
} |
|
|
|
|
|
|
|
.date-header-cell { |
|
|
|
width: 160rpx; |
|
|
|
min-width: 160rpx; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 课程项目 |
|
|
|
.course-item { |
|
|
|
width: 100%; |
|
|
|
@ -998,6 +1394,12 @@ export default { |
|
|
|
margin-bottom: 3rpx; |
|
|
|
} |
|
|
|
|
|
|
|
.course-time { |
|
|
|
color: #29d3b4; |
|
|
|
font-size: 22rpx; |
|
|
|
margin-bottom: 3rpx; |
|
|
|
} |
|
|
|
|
|
|
|
.course-students { |
|
|
|
color: #ccc; |
|
|
|
margin-bottom: 3rpx; |
|
|
|
|