You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1468 lines
41 KiB
1468 lines
41 KiB
<!-- 课程安排表 -->
|
|
<template>
|
|
<view class="schedule-container">
|
|
<!-- 顶部筛选区域 -->
|
|
<view class="filter-header">
|
|
<view class="filter-tabs">
|
|
<view
|
|
:class="['filter-tab',activeFilter === 'time' ? 'active' : '']"
|
|
@click="openFilter('time')"
|
|
>
|
|
时间
|
|
</view>
|
|
<view
|
|
:class="['filter-tab',activeFilter === 'teacher' ? 'active' : '']"
|
|
@click="openFilter('teacher')"
|
|
>
|
|
老师
|
|
</view>
|
|
<view
|
|
:class="['filter-tab',activeFilter === 'classroom' ? 'active' : '']"
|
|
@click="openFilter('classroom')"
|
|
>
|
|
教室
|
|
</view>
|
|
<view
|
|
:class="['filter-tab',activeFilter === 'class' ? 'active' : '']"
|
|
@click="openFilter('class')"
|
|
>
|
|
班级
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 筛选按钮 -->
|
|
<view class="filter-btn" @click="showFilterModal = true">
|
|
<fui-icon name="filter" size="20" color="#29d3b4"></fui-icon>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 日期导航 -->
|
|
<view class="date-nav">
|
|
<view class="nav-btn" @click="prevWeek">
|
|
<fui-icon name="arrowleft" size="16" color="#29d3b4"></fui-icon>
|
|
</view>
|
|
<view class="date-range">
|
|
{{ formatDateRange() }}
|
|
</view>
|
|
<view class="nav-btn" @click="nextWeek">
|
|
<fui-icon name="arrowright" size="16" color="#29d3b4"></fui-icon>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 课程统计 -->
|
|
<view class="schedule-stats">
|
|
共 {{ totalCourses }} 节课,未点名 {{ unnamedCourses }} 节课
|
|
</view>
|
|
|
|
<!-- 课程表主体 -->
|
|
<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-top="scrollTop"
|
|
@scroll="onScroll"
|
|
:style="{ width: '100%' }"
|
|
>
|
|
<view class="scroll-content" :style="{ width: tableWidth + 'rpx', minWidth: '1400rpx' }">
|
|
<!-- 表头 - 日期行 -->
|
|
<view class="table-header">
|
|
<!-- 日期列 -->
|
|
<view
|
|
class="date-header-cell"
|
|
v-for="(date, index) in weekDates"
|
|
:key="index"
|
|
>
|
|
<view class="date-week">{{ date.weekName }}</view>
|
|
<view class="date-day">{{ date.dateStr }}</view>
|
|
<view class="date-courses">共{{ date.courseCount }}节课</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 课程内容区 -->
|
|
<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="['course-cell',!timeSlot.available ? 'cell-unavailable' : '']"
|
|
v-for="(date, dateIndex) in weekDates"
|
|
:key="dateIndex"
|
|
@click="timeSlot.available ? handleCellClick(timeSlot, date) : null"
|
|
>
|
|
<!-- 课程项目 -->
|
|
<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"
|
|
v-for="(date, dateIndex) in weekDates"
|
|
:key="dateIndex"
|
|
@click="handleCellClick({time: ''}, date, teacher.id)"
|
|
>
|
|
<!-- 课程项目 -->
|
|
<view
|
|
class="course-item"
|
|
v-for="course in getCoursesByTeacherAndDate(teacher.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-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>
|
|
</view>
|
|
</scroll-view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 添加按钮 -->
|
|
<view class="add-btn" @click="addCourse">
|
|
<fui-icon name="add" size="24" color="#fff"></fui-icon>
|
|
</view>
|
|
|
|
<!-- 筛选弹窗 -->
|
|
<fui-modal
|
|
:show="showFilterModal"
|
|
title="筛选条件"
|
|
@cancel="closeFilterModal"
|
|
:buttons="[
|
|
{ text: '重置', type: 'secondary' },
|
|
{ text: '确定', type: 'primary' }
|
|
]"
|
|
@click="handleFilterConfirm"
|
|
>
|
|
<view class="filter-content">
|
|
<!-- 时间筛选 -->
|
|
<view class="filter-section">
|
|
<view class="filter-title">时间范围</view>
|
|
<view class="filter-options">
|
|
<view
|
|
class="filter-option"
|
|
:class="[selectedTimeRange === option.value ? 'selected' : '']"
|
|
v-for="option in timeRangeOptions"
|
|
:key="option.value"
|
|
@click="selectedTimeRange = option.value"
|
|
>
|
|
{{ option.label }}
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 老师筛选 -->
|
|
<view class="filter-section">
|
|
<view class="filter-title">授课老师</view>
|
|
<view class="filter-options">
|
|
<view
|
|
class="filter-option"
|
|
:class="[selectedTeachers.includes(teacher.id) ? 'selected' : '']"
|
|
v-for="teacher in teacherOptions"
|
|
:key="teacher.id"
|
|
@click="toggleTeacher(teacher.id)"
|
|
>
|
|
{{ teacher.name }}
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 场地筛选 -->
|
|
<view class="filter-section">
|
|
<view class="filter-title">选择场地</view>
|
|
<view class="filter-options">
|
|
<view
|
|
class="filter-option"
|
|
:class="[selectedVenueId === venue.id ? 'selected' : '']"
|
|
v-for="venue in venues"
|
|
:key="venue.id"
|
|
@click="selectVenue(venue.id)"
|
|
>
|
|
{{ venue.name }}
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 场地筛选 -->
|
|
<view class="filter-section">
|
|
<view class="filter-title">上课场地</view>
|
|
<view class="filter-options">
|
|
<view
|
|
class="filter-option"
|
|
:class="[selectedVenues.includes(venue.id) ? 'selected' : '']"
|
|
v-for="venue in venues"
|
|
:key="venue.id"
|
|
@click="toggleVenue(venue.id)"
|
|
>
|
|
{{ venue.name }}
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 班级筛选 -->
|
|
<view class="filter-section">
|
|
<view class="filter-title">班级</view>
|
|
<view class="filter-options">
|
|
<view
|
|
class="filter-option"
|
|
:class="[selectedClasses.includes(cls.id) ? 'selected' : '']"
|
|
v-for="cls in classOptions"
|
|
:key="cls.id"
|
|
@click="toggleClass(cls.id)"
|
|
>
|
|
{{ cls.name }}
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</fui-modal>
|
|
|
|
<!-- 课程详情弹窗 -->
|
|
<schedule-detail
|
|
:visible="showScheduleDetail"
|
|
:scheduleId="selectedScheduleId"
|
|
@update:visible="showScheduleDetail = $event"
|
|
@sign-in="handleSignIn"
|
|
@adjust-class="handleAdjustClass"
|
|
></schedule-detail>
|
|
</view>
|
|
</template>
|
|
|
|
<script>
|
|
import api from '@/api/apiRoute.js'
|
|
import ScheduleDetail from '@/components/schedule/ScheduleDetail.vue'
|
|
|
|
export default {
|
|
components: {
|
|
ScheduleDetail
|
|
},
|
|
data() {
|
|
return {
|
|
// 当前显示的周
|
|
currentWeekStart: null,
|
|
|
|
// 筛选相关
|
|
activeFilter: '',
|
|
showFilterModal: false,
|
|
selectedTimeRange: '',
|
|
selectedTeachers: [],
|
|
selectedVenues: [],
|
|
selectedClasses: [],
|
|
|
|
// 滚动相关
|
|
scrollLeft: 0,
|
|
scrollTop: 0,
|
|
scrollAnimationFrame: null, // 滚动动画帧
|
|
|
|
// 表格配置
|
|
tableWidth: 1500, // 表格总宽度,确保7天都能显示 (7*180+120=1380rpx)
|
|
|
|
// 时间段配置(动态生成,支持场地时间限制)
|
|
timeSlots: [],
|
|
|
|
// 场地信息
|
|
venues: [],
|
|
|
|
// 当前选中的场地
|
|
selectedVenueId: null,
|
|
|
|
// 筛选选项
|
|
timeRangeOptions: [
|
|
{ label: '全部时间', value: '' },
|
|
{ label: '上午', value: 'morning' },
|
|
{ label: '下午', value: 'afternoon' },
|
|
{ label: '晚上', value: 'evening' },
|
|
],
|
|
|
|
// 筛选选项数据
|
|
teacherOptions: [],
|
|
classOptions: [],
|
|
|
|
// 课程数据
|
|
courses: [],
|
|
|
|
// 加载状态
|
|
loading: false,
|
|
|
|
// 课程详情弹窗
|
|
selectedScheduleId: null,
|
|
showScheduleDetail: false,
|
|
|
|
// 筛选参数
|
|
filterParams: {
|
|
start_date: '',
|
|
end_date: '',
|
|
coach_id: '',
|
|
venue_id: '',
|
|
class_id: '',
|
|
time_range: '',
|
|
},
|
|
}
|
|
},
|
|
|
|
computed: {
|
|
// 当前周的日期
|
|
weekDates() {
|
|
const dates = []
|
|
const startDate = new Date(this.currentWeekStart)
|
|
|
|
for (let i = 0; i < 7; i++) {
|
|
const date = new Date(startDate)
|
|
date.setDate(startDate.getDate() + i)
|
|
|
|
const weekNames = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']
|
|
const dateStr = String(date.getMonth() + 1).padStart(2, '0') + '-' + String(date.getDate()).padStart(2, '0')
|
|
|
|
dates.push({
|
|
date: date.toISOString().split('T')[0],
|
|
weekName: weekNames[date.getDay()],
|
|
dateStr: dateStr,
|
|
courseCount: this.getCourseCountByDate(date.toISOString().split('T')[0]),
|
|
})
|
|
}
|
|
|
|
return dates
|
|
},
|
|
|
|
// 总课程数
|
|
totalCourses() {
|
|
return this.courses.length
|
|
},
|
|
|
|
// 未点名课程数
|
|
unnamedCourses() {
|
|
return this.courses.filter(course => course.status === '未点名').length
|
|
},
|
|
},
|
|
|
|
mounted() {
|
|
this.initCurrentWeek()
|
|
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: {
|
|
// 初始化当前周
|
|
initCurrentWeek() {
|
|
const today = new Date()
|
|
const currentDay = today.getDay() // 0是周日,1是周一
|
|
|
|
// 计算本周周一的日期
|
|
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]
|
|
|
|
// 设置筛选参数的日期范围
|
|
const endDate = new Date(this.currentWeekStart)
|
|
endDate.setDate(endDate.getDate() + 6)
|
|
|
|
this.filterParams.start_date = this.currentWeekStart
|
|
this.filterParams.end_date = endDate.toISOString().split('T')[0]
|
|
},
|
|
|
|
// 初始化时间段(基于场地可用时间)
|
|
initTimeSlots() {
|
|
// 如果没有选中场地,使用默认时间段
|
|
if (!this.selectedVenueId) {
|
|
this.generateDefaultTimeSlots()
|
|
return
|
|
}
|
|
|
|
const venue = this.venues.find(v => v.id === this.selectedVenueId)
|
|
if (!venue) {
|
|
this.generateDefaultTimeSlots()
|
|
return
|
|
}
|
|
|
|
this.timeSlots = this.generateVenueTimeSlots(venue)
|
|
},
|
|
|
|
// 生成默认时间段
|
|
generateDefaultTimeSlots() {
|
|
this.timeSlots = []
|
|
for (let hour = 9; hour <= 21; hour++) {
|
|
this.timeSlots.push({
|
|
time: `${hour.toString().padStart(2, '0')}:00`,
|
|
value: hour,
|
|
available: true,
|
|
timeSlot: `${hour.toString().padStart(2, '0')}:00-${(hour + 1).toString().padStart(2, '0')}:00`,
|
|
})
|
|
}
|
|
},
|
|
|
|
// 根据场地生成时间段
|
|
generateVenueTimeSlots(venue) {
|
|
const slots = []
|
|
|
|
if (venue.time_range_type === 'all') {
|
|
// 全天可用
|
|
for (let hour = 0; hour < 24; hour++) {
|
|
slots.push({
|
|
time: `${hour.toString().padStart(2, '0')}:00`,
|
|
value: hour,
|
|
available: true,
|
|
timeSlot: `${hour.toString().padStart(2, '0')}:00-${(hour + 1).toString().padStart(2, '0')}:00`,
|
|
})
|
|
}
|
|
} else if (venue.time_range_type === 'range') {
|
|
// 范围类型
|
|
const startHour = parseInt(venue.time_range_start.split(':')[0])
|
|
const endHour = parseInt(venue.time_range_end.split(':')[0])
|
|
|
|
// 生成全天时间段,标记可用状态
|
|
for (let hour = 8; hour <= 22; hour++) {
|
|
slots.push({
|
|
time: `${hour.toString().padStart(2, '0')}:00`,
|
|
value: hour,
|
|
available: hour >= startHour && hour < endHour,
|
|
timeSlot: `${hour.toString().padStart(2, '0')}:00-${(hour + 1).toString().padStart(2, '0')}:00`,
|
|
})
|
|
}
|
|
} else if (venue.time_range_type === 'fixed') {
|
|
// 固定时间段类型
|
|
const availableHours = new Set()
|
|
|
|
venue.fixed_time_ranges.forEach(range => {
|
|
const [start, end] = range.split('-')
|
|
const startHour = parseInt(start.split(':')[0])
|
|
const endHour = parseInt(end.split(':')[0])
|
|
|
|
for (let hour = startHour; hour < endHour; hour++) {
|
|
availableHours.add(hour)
|
|
}
|
|
})
|
|
|
|
// 生成全天时间段,标记可用状态
|
|
for (let hour = 8; hour <= 22; hour++) {
|
|
slots.push({
|
|
time: `${hour.toString().padStart(2, '0')}:00`,
|
|
value: hour,
|
|
available: availableHours.has(hour),
|
|
timeSlot: `${hour.toString().padStart(2, '0')}:00-${(hour + 1).toString().padStart(2, '0')}:00`,
|
|
})
|
|
}
|
|
}
|
|
|
|
return slots
|
|
},
|
|
|
|
// 格式化日期范围
|
|
formatDateRange() {
|
|
const start = new Date(this.currentWeekStart)
|
|
const end = new Date(start)
|
|
end.setDate(start.getDate() + 6)
|
|
|
|
const formatDate = (date) => {
|
|
return `${date.getFullYear()}年${String(date.getMonth() + 1).padStart(2, '0')}月${String(date.getDate()).padStart(2, '0')}日`
|
|
}
|
|
|
|
return `${formatDate(start)} - ${formatDate(end)}`
|
|
},
|
|
|
|
// 上一周
|
|
prevWeek() {
|
|
const currentStart = new Date(this.currentWeekStart)
|
|
currentStart.setDate(currentStart.getDate() - 7)
|
|
this.currentWeekStart = currentStart.toISOString().split('T')[0]
|
|
|
|
// 更新筛选参数的日期范围
|
|
const endDate = new Date(this.currentWeekStart)
|
|
endDate.setDate(endDate.getDate() + 6)
|
|
|
|
this.filterParams.start_date = this.currentWeekStart
|
|
this.filterParams.end_date = endDate.toISOString().split('T')[0]
|
|
|
|
// 重新加载数据
|
|
this.loadScheduleList()
|
|
},
|
|
|
|
// 下一周
|
|
nextWeek() {
|
|
const currentStart = new Date(this.currentWeekStart)
|
|
currentStart.setDate(currentStart.getDate() + 7)
|
|
this.currentWeekStart = currentStart.toISOString().split('T')[0]
|
|
|
|
// 更新筛选参数的日期范围
|
|
const endDate = new Date(this.currentWeekStart)
|
|
endDate.setDate(endDate.getDate() + 6)
|
|
|
|
this.filterParams.start_date = this.currentWeekStart
|
|
this.filterParams.end_date = endDate.toISOString().split('T')[0]
|
|
|
|
// 重新加载数据
|
|
this.loadScheduleList()
|
|
},
|
|
|
|
// 获取指定日期的课程数量
|
|
getCourseCountByDate(date) {
|
|
return this.courses.filter(course => course.date === date).length
|
|
},
|
|
|
|
// 获取指定时间和日期的课程
|
|
getCoursesByTimeAndDate(time, date) {
|
|
return this.courses.filter(course =>
|
|
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) {
|
|
// 如果已经是当前类型,则取消选中,否则切换为新类型
|
|
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);
|
|
}
|
|
}
|
|
},
|
|
|
|
// 关闭筛选弹窗
|
|
closeFilterModal() {
|
|
this.showFilterModal = false
|
|
},
|
|
|
|
// 筛选确认
|
|
async handleFilterConfirm(e) {
|
|
if (e.index === 0) {
|
|
// 重置
|
|
this.resetFilters()
|
|
} else if (e.index === 1) {
|
|
// 确定
|
|
this.applyFilters()
|
|
this.closeFilterModal()
|
|
|
|
// 重新加载数据后,重置滚动位置
|
|
await this.loadScheduleList()
|
|
this.scrollLeft = 0
|
|
this.scrollTop = 0
|
|
|
|
// 如果筛选改变了时间段,需要重新生成时间列
|
|
if (this.selectedTimeRange !== '' || this.selectedVenueId !== null) {
|
|
this.initTimeSlots()
|
|
}
|
|
}
|
|
},
|
|
|
|
// 重置筛选
|
|
resetFilters() {
|
|
this.selectedTimeRange = ''
|
|
this.selectedTeachers = []
|
|
this.selectedClassrooms = []
|
|
this.selectedClasses = []
|
|
},
|
|
|
|
// 应用筛选
|
|
applyFilters() {
|
|
// 更新筛选参数
|
|
this.filterParams.time_range = this.selectedTimeRange
|
|
|
|
// 处理教练筛选
|
|
if (this.selectedTeachers.length === 1) {
|
|
this.filterParams.coach_id = this.selectedTeachers[0]
|
|
} else if (this.selectedTeachers.length > 1) {
|
|
this.filterParams.coach_id = this.selectedTeachers
|
|
} else {
|
|
this.filterParams.coach_id = ''
|
|
}
|
|
|
|
// 处理场地筛选
|
|
if (this.selectedVenues.length === 1) {
|
|
this.filterParams.venue_id = this.selectedVenues[0]
|
|
} else if (this.selectedVenues.length > 1) {
|
|
this.filterParams.venue_id = this.selectedVenues
|
|
} else {
|
|
this.filterParams.venue_id = ''
|
|
}
|
|
|
|
// 处理班级筛选
|
|
if (this.selectedClasses.length === 1) {
|
|
this.filterParams.class_id = this.selectedClasses[0]
|
|
} else if (this.selectedClasses.length > 1) {
|
|
this.filterParams.class_id = this.selectedClasses
|
|
} else {
|
|
this.filterParams.class_id = ''
|
|
}
|
|
|
|
// 重新加载课程安排数据
|
|
this.loadScheduleList()
|
|
},
|
|
|
|
// 切换老师选择
|
|
toggleTeacher(teacherId) {
|
|
const index = this.selectedTeachers.indexOf(teacherId)
|
|
if (index > -1) {
|
|
this.selectedTeachers.splice(index, 1)
|
|
} else {
|
|
this.selectedTeachers.push(teacherId)
|
|
}
|
|
},
|
|
|
|
// 切换场地选择
|
|
toggleVenue(venueId) {
|
|
const index = this.selectedVenues.indexOf(venueId)
|
|
if (index > -1) {
|
|
this.selectedVenues.splice(index, 1)
|
|
} else {
|
|
this.selectedVenues.push(venueId)
|
|
}
|
|
},
|
|
|
|
// 切换班级选择
|
|
toggleClass(classId) {
|
|
const index = this.selectedClasses.indexOf(classId)
|
|
if (index > -1) {
|
|
this.selectedClasses.splice(index, 1)
|
|
} else {
|
|
this.selectedClasses.push(classId)
|
|
}
|
|
},
|
|
|
|
// 选择场地
|
|
selectVenue(venueId) {
|
|
this.selectedVenueId = venueId
|
|
this.filterParams.venue_id = venueId
|
|
this.initTimeSlots() // 重新初始化时间段
|
|
},
|
|
|
|
// 加载筛选选项
|
|
async loadFilterOptions() {
|
|
try {
|
|
// 直接使用已初始化的模拟数据
|
|
// 注释掉实际 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
|
|
|
|
// 设置教练选项
|
|
this.teacherOptions = data.coaches.map(coach => ({
|
|
id: coach.id,
|
|
name: coach.name,
|
|
avatar: coach.avatar,
|
|
}))
|
|
|
|
// 设置场地选项
|
|
this.venues = data.venues.map(venue => ({
|
|
id: venue.id,
|
|
name: venue.venue_name,
|
|
capacity: venue.capacity,
|
|
description: venue.description,
|
|
}))
|
|
|
|
// 设置班级选项
|
|
this.classOptions = data.classes.map(cls => ({
|
|
id: cls.id,
|
|
name: cls.class_name,
|
|
level: cls.class_level,
|
|
students: cls.total_students,
|
|
}))
|
|
} else {
|
|
uni.showToast({
|
|
title: res.msg || '加载筛选选项失败',
|
|
icon: 'none',
|
|
})
|
|
}
|
|
} catch (error) {
|
|
console.error('加载筛选选项失败:', error)
|
|
uni.showToast({
|
|
title: '加载筛选选项失败',
|
|
icon: 'none',
|
|
})
|
|
}
|
|
},
|
|
|
|
// 加载课程安排列表
|
|
async loadScheduleList() {
|
|
try {
|
|
this.loading = true
|
|
|
|
// 使用模拟数据 API
|
|
const res = await api.getCourseScheduleListMock(this.filterParams)
|
|
|
|
if (res.code === 1) {
|
|
// 转换数据格式
|
|
this.courses = res.data.list.map(item => ({
|
|
id: item.id,
|
|
date: item.course_date,
|
|
time: item.time_info.start_time,
|
|
courseName: item.course_name,
|
|
students: `已报名${item.enrolled_count}人`,
|
|
teacher: item.coach_name,
|
|
status: item.status_text,
|
|
type: this.getCourseType(item),
|
|
venue: item.venue_name,
|
|
duration: Math.floor(item.time_info.duration / 60),
|
|
time_slot: item.time_slot,
|
|
raw: item, // 保存原始数据
|
|
}))
|
|
} else {
|
|
uni.showToast({
|
|
title: res.msg || '加载课程安排列表失败',
|
|
icon: 'none',
|
|
})
|
|
}
|
|
} catch (error) {
|
|
console.error('加载课程安排列表失败:', error)
|
|
uni.showToast({
|
|
title: '加载课程安排列表失败',
|
|
icon: 'none',
|
|
})
|
|
} finally {
|
|
this.loading = false
|
|
}
|
|
},
|
|
|
|
// 获取课程类型
|
|
getCourseType(course) {
|
|
// 在这里判断课程类型,例如根据课程名称或其他属性
|
|
if (course.course_type === 'private') {
|
|
return 'private'
|
|
} else if (course.course_type === 'activity') {
|
|
return 'activity'
|
|
} else {
|
|
return 'normal'
|
|
}
|
|
},
|
|
|
|
// 滚动事件处理函数
|
|
onScroll(e) {
|
|
if (this.scrollAnimationFrame) {
|
|
cancelAnimationFrame(this.scrollAnimationFrame)
|
|
}
|
|
|
|
this.scrollAnimationFrame = requestAnimationFrame(() => {
|
|
// 获取滚动位置
|
|
if (e.detail.scrollLeft !== undefined) {
|
|
this.scrollLeft = e.detail.scrollLeft
|
|
}
|
|
|
|
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, 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 });
|
|
},
|
|
|
|
// 添加课程
|
|
addCourse() {
|
|
// 跳转到添加课程页面
|
|
uni.navigateTo({
|
|
url: '/pages/coach/schedule/add_schedule',
|
|
})
|
|
},
|
|
|
|
// 查看课程安排详情
|
|
viewScheduleDetail(scheduleId) {
|
|
// 显示课程详情弹窗
|
|
this.selectedScheduleId = scheduleId;
|
|
this.showScheduleDetail = true;
|
|
},
|
|
|
|
// 处理点名事件
|
|
handleSignIn(data) {
|
|
uni.navigateTo({
|
|
url: `/pages/coach/schedule/sign_in?id=${data.scheduleId}`,
|
|
})
|
|
},
|
|
|
|
// 处理调课事件
|
|
handleAdjustClass(data) {
|
|
uni.navigateTo({
|
|
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>
|
|
|
|
<style lang="less" scoped>
|
|
.schedule-container {
|
|
width: 100%;
|
|
height: 100vh;
|
|
background: #292929;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
// 顶部筛选区域
|
|
.filter-header {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 20rpx 30rpx;
|
|
background: #434544;
|
|
border-bottom: 1px solid #555;
|
|
}
|
|
|
|
.filter-tabs {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 40rpx;
|
|
}
|
|
|
|
.filter-tab {
|
|
padding: 16rpx 32rpx;
|
|
border-radius: 8rpx;
|
|
background: transparent;
|
|
color: #999;
|
|
font-size: 28rpx;
|
|
transition: all 0.3s ease;
|
|
|
|
&.active {
|
|
background: #29d3b4;
|
|
color: #fff;
|
|
}
|
|
}
|
|
|
|
.filter-btn {
|
|
padding: 16rpx;
|
|
border-radius: 8rpx;
|
|
background: rgba(41, 211, 180, 0.1);
|
|
}
|
|
|
|
// 日期导航
|
|
.date-nav {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 20rpx 30rpx;
|
|
background: #292929;
|
|
border-bottom: 1px solid #434544;
|
|
}
|
|
|
|
.nav-btn {
|
|
padding: 16rpx;
|
|
border-radius: 8rpx;
|
|
background: rgba(41, 211, 180, 0.1);
|
|
}
|
|
|
|
.date-range {
|
|
color: #fff;
|
|
font-size: 32rpx;
|
|
font-weight: 500;
|
|
}
|
|
|
|
// 课程统计
|
|
.schedule-stats {
|
|
padding: 20rpx 30rpx;
|
|
background: #292929;
|
|
color: #ff9500;
|
|
font-size: 28rpx;
|
|
border-bottom: 1px solid #434544;
|
|
}
|
|
|
|
// 课程表主体
|
|
.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;
|
|
}
|
|
|
|
.schedule-scroll-horizontal {
|
|
width: 100%;
|
|
height: 100%;
|
|
overflow: scroll;
|
|
}
|
|
|
|
.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: 1;
|
|
min-width: 1260rpx; /* 7天 * 180rpx = 1260rpx */
|
|
}
|
|
|
|
.date-header-cell {
|
|
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;
|
|
font-weight: 500;
|
|
margin-bottom: 5rpx;
|
|
}
|
|
|
|
.date-day {
|
|
font-size: 24rpx;
|
|
color: #ccc;
|
|
margin-bottom: 5rpx;
|
|
}
|
|
|
|
.date-courses {
|
|
font-size: 20rpx;
|
|
color: #29d3b4;
|
|
}
|
|
}
|
|
|
|
.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;
|
|
}
|
|
|
|
.course-cell {
|
|
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;
|
|
opacity: 0.3;
|
|
pointer-events: none;
|
|
|
|
&::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 0;
|
|
right: 0;
|
|
height: 1px;
|
|
background: #555;
|
|
transform: translateY(-50%);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 响应式适配
|
|
@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%;
|
|
padding: 8rpx;
|
|
border-radius: 6rpx;
|
|
font-size: 20rpx;
|
|
line-height: 1.3;
|
|
margin-bottom: 5rpx;
|
|
|
|
&.course-normal {
|
|
background: rgba(41, 211, 180, 0.2);
|
|
border: 1px solid #29d3b4;
|
|
}
|
|
|
|
&.course-private {
|
|
background: rgba(255, 149, 0, 0.2);
|
|
border: 1px solid #ff9500;
|
|
}
|
|
|
|
&.course-activity {
|
|
background: rgba(255, 59, 48, 0.2);
|
|
border: 1px solid #ff3b30;
|
|
}
|
|
}
|
|
|
|
.course-name {
|
|
color: #fff;
|
|
font-weight: 500;
|
|
margin-bottom: 3rpx;
|
|
}
|
|
|
|
.course-time {
|
|
color: #29d3b4;
|
|
font-size: 22rpx;
|
|
margin-bottom: 3rpx;
|
|
}
|
|
|
|
.course-students {
|
|
color: #ccc;
|
|
margin-bottom: 3rpx;
|
|
}
|
|
|
|
.course-teacher {
|
|
color: #ccc;
|
|
margin-bottom: 3rpx;
|
|
}
|
|
|
|
.course-status {
|
|
color: #29d3b4;
|
|
}
|
|
|
|
// 添加按钮
|
|
.add-btn {
|
|
position: fixed;
|
|
bottom: 120rpx;
|
|
right: 30rpx;
|
|
width: 100rpx;
|
|
height: 100rpx;
|
|
border-radius: 50%;
|
|
background: #29d3b4;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
box-shadow: 0 4rpx 20rpx rgba(41, 211, 180, 0.3);
|
|
}
|
|
|
|
// 筛选弹窗内容
|
|
.filter-content {
|
|
padding: 0 20rpx;
|
|
}
|
|
|
|
.filter-section {
|
|
margin-bottom: 40rpx;
|
|
}
|
|
|
|
.filter-title {
|
|
color: #333;
|
|
font-size: 30rpx;
|
|
font-weight: 500;
|
|
margin-bottom: 20rpx;
|
|
}
|
|
|
|
.filter-options {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 20rpx;
|
|
}
|
|
|
|
.filter-option {
|
|
padding: 16rpx 24rpx;
|
|
border: 1px solid #ddd;
|
|
border-radius: 6rpx;
|
|
background: #f8f8f8;
|
|
color: #333;
|
|
font-size: 26rpx;
|
|
|
|
&.selected {
|
|
border-color: #29d3b4;
|
|
background: #29d3b4;
|
|
color: #fff;
|
|
}
|
|
}
|
|
</style>
|