From 0d052ceac00ce239d37b8e1e644674661e7b6e1b Mon Sep 17 00:00:00 2001
From: zeyan <258785420@qq.com>
Date: Fri, 4 Jul 2025 09:25:38 +0800
Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=20bug?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../CourseScheduleController.php | 217 ++++
.../api/apiService/CourseScheduleService.php | 567 +++++++++
uniapp/pages/coach/schedule/README.md | 175 +++
.../pages/coach/schedule/schedule_table.vue | 1036 +++++++++++++++++
4 files changed, 1995 insertions(+)
create mode 100644 niucloud/app/api/controller/apiController/CourseScheduleController.php
create mode 100644 niucloud/app/service/api/apiService/CourseScheduleService.php
create mode 100644 uniapp/pages/coach/schedule/README.md
create mode 100644 uniapp/pages/coach/schedule/schedule_table.vue
diff --git a/niucloud/app/api/controller/apiController/CourseScheduleController.php b/niucloud/app/api/controller/apiController/CourseScheduleController.php
new file mode 100644
index 00000000..e468031c
--- /dev/null
+++ b/niucloud/app/api/controller/apiController/CourseScheduleController.php
@@ -0,0 +1,217 @@
+request->get();
+ $service = new CourseScheduleService();
+ return success('获取成功', $service->getScheduleList($data));
+ }
+
+ /**
+ * 获取课程安排详情
+ */
+ public function getScheduleInfo()
+ {
+ $scheduleId = $this->request->get('schedule_id', 0);
+ if (empty($scheduleId)) {
+ return fail('课程安排ID不能为空');
+ }
+
+ $service = new CourseScheduleService();
+ $result = $service->getScheduleInfo($scheduleId);
+
+ if (isset($result['code']) && $result['code'] == 0) {
+ return fail($result['msg'] ?? '获取课程安排详情失败');
+ }
+
+ return success('获取成功', $result);
+ }
+
+ /**
+ * 创建课程安排
+ */
+ public function createSchedule()
+ {
+ $data = $this->request->post();
+ $service = new CourseScheduleService();
+ $result = $service->createSchedule($data);
+
+ if ($result['code'] == 1) {
+ return success($result['msg'], $result['data']);
+ } else {
+ return fail($result['msg']);
+ }
+ }
+
+ /**
+ * 批量创建课程安排
+ */
+ public function batchCreateSchedule()
+ {
+ $data = $this->request->post();
+ $service = new CourseScheduleService();
+ $result = $service->batchCreateSchedule($data);
+
+ if ($result['code'] == 1) {
+ return success($result['msg'], $result['data']);
+ } else {
+ return fail($result['msg']);
+ }
+ }
+
+ /**
+ * 更新课程安排
+ */
+ public function updateSchedule()
+ {
+ $data = $this->request->post();
+ $scheduleId = $data['schedule_id'] ?? 0;
+
+ if (empty($scheduleId)) {
+ return fail('课程安排ID不能为空');
+ }
+
+ $service = new CourseScheduleService();
+ $result = $service->updateSchedule($scheduleId, $data);
+
+ if ($result['code'] == 1) {
+ return success($result['msg'], $result['data']);
+ } else {
+ return fail($result['msg']);
+ }
+ }
+
+ /**
+ * 删除课程安排
+ */
+ public function deleteSchedule()
+ {
+ $scheduleId = $this->request->post('schedule_id', 0);
+ if (empty($scheduleId)) {
+ return fail('课程安排ID不能为空');
+ }
+
+ $service = new CourseScheduleService();
+ $result = $service->deleteSchedule($scheduleId);
+
+ if ($result['code'] == 1) {
+ return success($result['msg']);
+ } else {
+ return fail($result['msg']);
+ }
+ }
+
+ /**
+ * 获取场地列表
+ */
+ public function getVenueList()
+ {
+ $data = $this->request->get();
+ $service = new CourseScheduleService();
+ return success('获取成功', $service->getVenueList($data));
+ }
+
+ /**
+ * 获取场地可用时间
+ */
+ public function getVenueAvailableTime()
+ {
+ $venueId = $this->request->get('venue_id', 0);
+ $date = $this->request->get('date', '');
+
+ if (empty($venueId)) {
+ return fail('场地ID不能为空');
+ }
+
+ if (empty($date)) {
+ return fail('日期不能为空');
+ }
+
+ $service = new CourseScheduleService();
+ return success('获取成功', $service->getVenueAvailableTime($venueId, $date));
+ }
+
+ /**
+ * 检查教练时间冲突
+ */
+ public function checkCoachConflict()
+ {
+ $data = $this->request->get();
+ $service = new CourseScheduleService();
+ return success('检查完成', $service->checkCoachConflict($data));
+ }
+
+ /**
+ * 获取课程安排统计
+ */
+ public function getScheduleStatistics()
+ {
+ $data = $this->request->get();
+ $service = new CourseScheduleService();
+ return success('获取成功', $service->getScheduleStatistics($data));
+ }
+
+ /**
+ * 学员加入课程安排
+ */
+ public function joinSchedule()
+ {
+ $data = $this->request->post();
+ $service = new CourseScheduleService();
+ $result = $service->joinSchedule($data);
+
+ if ($result['code'] == 1) {
+ return success($result['msg'], $result['data']);
+ } else {
+ return fail($result['msg']);
+ }
+ }
+
+ /**
+ * 学员退出课程安排
+ */
+ public function leaveSchedule()
+ {
+ $data = $this->request->post();
+ $service = new CourseScheduleService();
+ $result = $service->leaveSchedule($data);
+
+ if ($result['code'] == 1) {
+ return success($result['msg']);
+ } else {
+ return fail($result['msg']);
+ }
+ }
+
+ /**
+ * 获取筛选选项(教练、课程、班级等)
+ */
+ public function getFilterOptions()
+ {
+ $data = $this->request->get();
+ $service = new CourseScheduleService();
+ return success('获取成功', $service->getFilterOptions($data));
+ }
+}
\ No newline at end of file
diff --git a/niucloud/app/service/api/apiService/CourseScheduleService.php b/niucloud/app/service/api/apiService/CourseScheduleService.php
new file mode 100644
index 00000000..9da286bd
--- /dev/null
+++ b/niucloud/app/service/api/apiService/CourseScheduleService.php
@@ -0,0 +1,567 @@
+buildScheduleWhere($data);
+
+ // 分页参数
+ $page = intval($data['page'] ?? 1);
+ $limit = intval($data['limit'] ?? 20);
+ $offset = ($page - 1) * $limit;
+
+ // 基础查询
+ $query = Db::name($this->prefix . 'course_schedule')
+ ->alias('cs')
+ ->leftJoin($this->prefix . 'course c', 'cs.course_id = c.id')
+ ->leftJoin($this->prefix . 'venue v', 'cs.venue_id = v.id')
+ ->leftJoin($this->prefix . 'campus cap', 'cs.campus_id = cap.id')
+ ->leftJoin($this->prefix . 'personnel coach', 'cs.coach_id = coach.id')
+ ->leftJoin($this->prefix . 'personnel edu', 'cs.education_id = edu.id')
+ ->where($where)
+ ->where('cs.deleted_at', 0);
+
+ // 获取总数
+ $total = $query->count();
+
+ // 获取列表数据
+ $list = $query->field([
+ 'cs.*',
+ 'c.course_name',
+ 'c.course_type',
+ 'c.duration as course_duration',
+ 'c.session_count',
+ 'c.single_session_count',
+ 'v.venue_name',
+ 'v.capacity as venue_capacity',
+ 'cap.campus_name',
+ 'coach.name as coach_name',
+ 'coach.head_img as coach_avatar',
+ 'edu.name as education_name'
+ ])
+ ->order('cs.course_date DESC, cs.time_slot ASC')
+ ->limit($offset, $limit)
+ ->select()
+ ->toArray();
+
+ // 处理列表数据
+ foreach ($list as &$item) {
+ // 解析时间段
+ $item['time_info'] = $this->parseTimeSlot($item['time_slot']);
+
+ // 获取参与学员信息
+ $item['students'] = $this->getScheduleStudents($item['id']);
+
+ // 获取助教信息
+ $item['assistants'] = $this->getScheduleAssistants($item['assistant_ids']);
+
+ // 计算已报名人数
+ $item['enrolled_count'] = count($item['students']);
+
+ // 计算剩余容量
+ $item['remaining_capacity'] = max(0, ($item['available_capacity'] ?? $item['venue_capacity']) - $item['enrolled_count']);
+
+ // 格式化状态
+ $item['status_text'] = $this->getStatusText($item['status']);
+
+ // 格式化创建方式
+ $item['created_by_text'] = $item['created_by'] == 'manual' ? '手动安排' : '系统创建';
+
+ // 处理图片路径
+ $item['coach_avatar'] = $item['coach_avatar'] ? $this->formatImageUrl($item['coach_avatar']) : '';
+ }
+
+ return [
+ 'list' => $list,
+ 'total' => $total,
+ 'page' => $page,
+ 'limit' => $limit,
+ 'pages' => ceil($total / $limit)
+ ];
+
+ } catch (\Exception $e) {
+ return [
+ 'list' => [],
+ 'total' => 0,
+ 'page' => 1,
+ 'limit' => $limit ?? 20,
+ 'pages' => 0,
+ 'error' => $e->getMessage()
+ ];
+ }
+ }
+
+ /**
+ * 构建查询条件
+ * @param array $data 筛选参数
+ * @return array 条件数组
+ */
+ private function buildScheduleWhere($data)
+ {
+ $where = [];
+
+ // 日期范围筛选
+ if (!empty($data['start_date'])) {
+ $where[] = ['cs.course_date', '>=', $data['start_date']];
+ }
+ if (!empty($data['end_date'])) {
+ $where[] = ['cs.course_date', '<=', $data['end_date']];
+ }
+
+ // 校区筛选
+ if (!empty($data['campus_id'])) {
+ if (is_array($data['campus_id'])) {
+ $where[] = ['cs.campus_id', 'in', $data['campus_id']];
+ } else {
+ $where[] = ['cs.campus_id', '=', $data['campus_id']];
+ }
+ }
+
+ // 场地筛选
+ if (!empty($data['venue_id'])) {
+ if (is_array($data['venue_id'])) {
+ $where[] = ['cs.venue_id', 'in', $data['venue_id']];
+ } else {
+ $where[] = ['cs.venue_id', '=', $data['venue_id']];
+ }
+ }
+
+ // 教练筛选
+ if (!empty($data['coach_id'])) {
+ if (is_array($data['coach_id'])) {
+ $where[] = ['cs.coach_id', 'in', $data['coach_id']];
+ } else {
+ $where[] = ['cs.coach_id', '=', $data['coach_id']];
+ }
+ }
+
+ // 课程筛选
+ if (!empty($data['course_id'])) {
+ if (is_array($data['course_id'])) {
+ $where[] = ['cs.course_id', 'in', $data['course_id']];
+ } else {
+ $where[] = ['cs.course_id', '=', $data['course_id']];
+ }
+ }
+
+ // 状态筛选
+ if (!empty($data['status'])) {
+ if (is_array($data['status'])) {
+ $where[] = ['cs.status', 'in', $data['status']];
+ } else {
+ $where[] = ['cs.status', '=', $data['status']];
+ }
+ }
+
+ // 教务筛选
+ if (!empty($data['education_id'])) {
+ $where[] = ['cs.education_id', '=', $data['education_id']];
+ }
+
+ // 时间段筛选
+ if (!empty($data['time_range'])) {
+ switch ($data['time_range']) {
+ case 'morning':
+ $where[] = ['cs.time_slot', 'like', '0%'];
+ break;
+ case 'afternoon':
+ $where[] = ['cs.time_slot', 'like', '1%'];
+ break;
+ case 'evening':
+ $where[] = ['cs.time_slot', 'like', '1[8-9]%'];
+ break;
+ }
+ }
+
+ // 自动排课筛选
+ if (isset($data['auto_schedule'])) {
+ $where[] = ['cs.auto_schedule', '=', $data['auto_schedule']];
+ }
+
+ // 创建方式筛选
+ if (!empty($data['created_by'])) {
+ $where[] = ['cs.created_by', '=', $data['created_by']];
+ }
+
+ return $where;
+ }
+
+ /**
+ * 解析时间段
+ * @param string $timeSlot 时间段字符串(格式如:09:00-10:30)
+ * @return array 解析后的时间段信息
+ */
+ private function parseTimeSlot($timeSlot)
+ {
+ if (strpos($timeSlot, '-') !== false) {
+ list($startTime, $endTime) = explode('-', $timeSlot);
+ return [
+ 'start_time' => trim($startTime),
+ 'end_time' => trim($endTime),
+ 'duration' => $this->calculateDuration(trim($startTime), trim($endTime))
+ ];
+ }
+
+ return [
+ 'start_time' => $timeSlot,
+ 'end_time' => '',
+ 'duration' => 60 // 默认1小时
+ ];
+ }
+
+ /**
+ * 计算时长(分钟)
+ * @param string $startTime 开始时间
+ * @param string $endTime 结束时间
+ * @return int 时长(分钟)
+ */
+ private function calculateDuration($startTime, $endTime)
+ {
+ try {
+ $start = strtotime($startTime);
+ $end = strtotime($endTime);
+ return ($end - $start) / 60;
+ } catch (\Exception $e) {
+ return 60; // 默认1小时
+ }
+ }
+
+ /**
+ * 获取课程安排的学员信息
+ * @param int $scheduleId 课程安排ID
+ * @return array 学员信息数组
+ */
+ private function getScheduleStudents($scheduleId)
+ {
+ try {
+ $students = Db::name($this->prefix . 'person_course_schedule')
+ ->alias('pcs')
+ ->leftJoin($this->prefix . 'student s', 'pcs.student_id = s.id')
+ ->leftJoin($this->prefix . 'customer_resources cr', 'pcs.resources_id = cr.id')
+ ->leftJoin($this->prefix . 'member m', 'cr.member_id = m.member_id')
+ ->where('pcs.schedule_id', $scheduleId)
+ ->where('pcs.deleted_at', 0)
+ ->field([
+ 'pcs.*',
+ 's.name as student_name',
+ 'cr.name as resource_name',
+ 'cr.phone_number',
+ 'cr.age',
+ 'cr.gender',
+ 'm.headimg as avatar'
+ ])
+ ->select()
+ ->toArray();
+
+ foreach ($students as &$student) {
+ $student['name'] = $student['student_name'] ?: $student['resource_name'];
+ $student['avatar'] = $student['avatar'] ? $this->formatImageUrl($student['avatar']) : '';
+ $student['status_text'] = $this->getStudentStatusText($student['status']);
+ $student['person_type_text'] = $student['person_type'] == 'student' ? '正式学员' : '体验学员';
+ $student['course_type_text'] = $this->getCourseTypeText($student['course_type']);
+ }
+
+ return $students;
+ } catch (\Exception $e) {
+ return [];
+ }
+ }
+
+ /**
+ * 获取助教信息
+ * @param string $assistantIds 助教ID字符串,使用逗号分隔
+ * @return array 助教信息数组
+ */
+ private function getScheduleAssistants($assistantIds)
+ {
+ if (empty($assistantIds)) {
+ return [];
+ }
+
+ try {
+ $ids = explode(',', $assistantIds);
+ $assistants = Db::name($this->prefix . 'personnel')
+ ->whereIn('id', $ids)
+ ->field('id, name, head_img, phone')
+ ->select()
+ ->toArray();
+
+ foreach ($assistants as &$assistant) {
+ $assistant['head_img'] = $assistant['head_img'] ? $this->formatImageUrl($assistant['head_img']) : '';
+ }
+
+ return $assistants;
+ } catch (\Exception $e) {
+ return [];
+ }
+ }
+
+ /**
+ * 获取状态文本
+ * @param string $status 状态码
+ * @return string 状态文本描述
+ */
+ private function getStatusText($status)
+ {
+ $statusMap = [
+ 'pending' => '待开始',
+ 'upcoming' => '即将开始',
+ 'ongoing' => '进行中',
+ 'completed' => '已结束'
+ ];
+
+ return $statusMap[$status] ?? $status;
+ }
+
+ /**
+ * 获取学员状态文本
+ * @param int $status 学员状态码
+ * @return string 学员状态文本描述
+ */
+ private function getStudentStatusText($status)
+ {
+ $statusMap = [
+ 0 => '待上课',
+ 1 => '已上课',
+ 2 => '请假'
+ ];
+
+ return $statusMap[$status] ?? '未知';
+ }
+
+ /**
+ * 获取课程类型文本
+ * @param int $courseType 课程类型码
+ * @return string 课程类型文本描述
+ */
+ private function getCourseTypeText($courseType)
+ {
+ $typeMap = [
+ 1 => '加课',
+ 2 => '补课',
+ 3 => '等待位'
+ ];
+
+ return $typeMap[$courseType] ?? '正常课程';
+ }
+
+ /**
+ * 格式化图片URL
+ * @param string $imagePath 图片路径
+ * @return string 格式化后的图片URL
+ */
+ private function formatImageUrl($imagePath)
+ {
+ if (empty($imagePath)) {
+ return '';
+ }
+
+ // 如果已经是完整URL,直接返回
+ if (strpos($imagePath, 'http') === 0) {
+ return $imagePath;
+ }
+
+ // 拼接域名
+ $domain = request()->domain();
+ return $domain . '/' . ltrim($imagePath, '/');
+ }
+
+ /**
+ * 获取筛选选项(教练、课程、班级等)
+ * @param array $data 请求参数
+ * @return array 筛选选项数据
+ */
+ public function getFilterOptions($data = [])
+ {
+ try {
+ $result = [
+ 'coaches' => [], // 教练列表
+ 'courses' => [], // 课程列表
+ 'classes' => [], // 班级列表
+ 'venues' => [], // 场地列表
+ 'campuses' => [], // 校区列表
+ 'status_options' => [] // 状态选项
+ ];
+
+ // 获取教练列表
+ $result['coaches'] = Db::name($this->prefix . 'personnel')
+ ->where('is_coach', 1)
+ ->where('deleted_at', 0)
+ ->field('id, name, head_img as avatar, phone')
+ ->select()
+ ->toArray();
+
+ foreach ($result['coaches'] as &$coach) {
+ $coach['avatar'] = $coach['avatar'] ? $this->formatImageUrl($coach['avatar']) : '';
+ }
+
+ // 获取课程列表
+ $result['courses'] = Db::name($this->prefix . 'course')
+ ->where('deleted_at', 0)
+ ->field('id, course_name, course_type, duration')
+ ->select()
+ ->toArray();
+
+ // 获取班级列表
+ $result['classes'] = Db::name($this->prefix . 'class')
+ ->where('deleted_at', 0)
+ ->field('id, class_name, class_level, total_students')
+ ->select()
+ ->toArray();
+
+ // 获取场地列表
+ $result['venues'] = Db::name($this->prefix . 'venue')
+ ->where('deleted_at', 0)
+ ->field('id, venue_name, capacity, description')
+ ->select()
+ ->toArray();
+
+ // 获取校区列表
+ $result['campuses'] = Db::name($this->prefix . 'campus')
+ ->where('deleted_at', 0)
+ ->field('id, campus_name, address')
+ ->select()
+ ->toArray();
+
+ // 状态选项
+ $result['status_options'] = [
+ ['value' => 'pending', 'label' => '待开始'],
+ ['value' => 'upcoming', 'label' => '即将开始'],
+ ['value' => 'ongoing', 'label' => '进行中'],
+ ['value' => 'completed', 'label' => '已结束']
+ ];
+
+ return $result;
+
+ } catch (\Exception $e) {
+ return [
+ 'coaches' => [],
+ 'courses' => [],
+ 'classes' => [],
+ 'venues' => [],
+ 'campuses' => [],
+ 'status_options' => [],
+ 'error' => $e->getMessage()
+ ];
+ }
+ }
+
+ /**
+ * 获取课程安排详情
+ * @param int $scheduleId 课程安排ID
+ * @return array 课程安排详细信息或错误信息
+ */
+ public function getScheduleInfo($scheduleId)
+ {
+ try {
+ // 查询课程安排信息
+ $schedule = Db::name($this->prefix . 'course_schedule')
+ ->alias('cs')
+ ->leftJoin($this->prefix . 'course c', 'cs.course_id = c.id')
+ ->leftJoin($this->prefix . 'venue v', 'cs.venue_id = v.id')
+ ->leftJoin($this->prefix . 'campus cap', 'cs.campus_id = cap.id')
+ ->leftJoin($this->prefix . 'personnel coach', 'cs.coach_id = coach.id')
+ ->leftJoin($this->prefix . 'personnel edu', 'cs.education_id = edu.id')
+ ->where('cs.id', $scheduleId)
+ ->where('cs.deleted_at', 0)
+ ->field([
+ 'cs.*',
+ 'c.course_name',
+ 'c.course_type',
+ 'c.duration as course_duration',
+ 'c.session_count',
+ 'c.single_session_count',
+ 'v.venue_name',
+ 'v.capacity as venue_capacity',
+ 'cap.campus_name',
+ 'coach.name as coach_name',
+ 'coach.head_img as coach_avatar',
+ 'edu.name as education_name'
+ ])
+ ->find();
+
+ if (empty($schedule)) {
+ return ['code' => 0, 'msg' => '课程安排不存在或已被删除'];
+ }
+
+ // 解析时间段
+ $schedule['time_info'] = $this->parseTimeSlot($schedule['time_slot']);
+
+ // 获取参与学员信息
+ $schedule['students'] = $this->getScheduleStudents($schedule['id']);
+
+ // 获取助教信息
+ $schedule['assistants'] = $this->getScheduleAssistants($schedule['assistant_ids']);
+
+ // 计算已报名人数
+ $schedule['enrolled_count'] = count($schedule['students']);
+
+ // 计算剩余容量
+ $schedule['remaining_capacity'] = max(0, ($schedule['available_capacity'] ?? $schedule['venue_capacity']) - $schedule['enrolled_count']);
+
+ // 格式化状态
+ $schedule['status_text'] = $this->getStatusText($schedule['status']);
+
+ // 格式化创建方式
+ $schedule['created_by_text'] = $schedule['created_by'] == 'manual' ? '手动安排' : '系统创建';
+
+ // 处理图片路径
+ $schedule['coach_avatar'] = $schedule['coach_avatar'] ? $this->formatImageUrl($schedule['coach_avatar']) : '';
+
+ // 获取班级相关信息
+ if (!empty($schedule['class_id'])) {
+ $schedule['class_info'] = Db::name($this->prefix . 'class')
+ ->where('id', $schedule['class_id'])
+ ->field('id, class_name, class_level, total_students')
+ ->find();
+ } else {
+ $schedule['class_info'] = null;
+ }
+
+ // 获取历史变更记录
+ $schedule['change_history'] = Db::name($this->prefix . 'course_schedule_changes')
+ ->where('schedule_id', $scheduleId)
+ ->order('created_at DESC')
+ ->select()
+ ->toArray();
+
+ return $schedule;
+
+ } catch (\Exception $e) {
+ return ['code' => 0, 'msg' => $e->getMessage()];
+ }
+ }
+}
\ No newline at end of file
diff --git a/uniapp/pages/coach/schedule/README.md b/uniapp/pages/coach/schedule/README.md
new file mode 100644
index 00000000..dc9aa09f
--- /dev/null
+++ b/uniapp/pages/coach/schedule/README.md
@@ -0,0 +1,175 @@
+# 课程安排表组件
+
+## 功能特性
+
+### 📅 完整的课程表视图
+- **时间轴显示**:11:00-17:00 每小时时间段
+- **周视图**:显示一周7天的课程安排
+- **双向滚动**:支持水平(日期)和垂直(时间)滚动
+- **响应式布局**:适配微信小程序端
+
+### 🔍 智能筛选功能
+- **顶部快捷筛选**:时间、老师、教室、班级
+- **详细筛选弹窗**:支持多选条件组合筛选
+- **实时统计**:显示总课程数和未点名课程数
+
+### 🎨 主题样式
+- **暗黑主题**:#292929 背景色
+- **绿色主色调**:#29d3b4 强调色
+- **层次化设计**:清晰的视觉层级
+
+### 📱 交互体验
+- **日期导航**:支持上一周/下一周切换
+- **课程类型**:普通课程、私教课程、活动课程区分显示
+- **点击交互**:支持单元格点击添加课程
+- **悬浮按钮**:快速添加课程
+
+## 使用方法
+
+### 1. 页面导航
+```javascript
+// 跳转到课程安排表
+this.$navigateTo({
+ url: '/pages/coach/schedule/schedule_table'
+})
+```
+
+### 2. 数据结构
+
+#### 课程数据格式
+```javascript
+{
+ id: 1, // 课程ID
+ date: '2025-07-02', // 日期
+ time: '11:00', // 时间
+ courseName: '花花-丁颖', // 课程名称
+ students: '小鱼-周子', // 学员
+ teacher: '燕子-符', // 老师
+ status: '燕菜', // 状态
+ type: 'normal', // 类型:normal/private/activity
+ duration: 1 // 持续时间(小时)
+}
+```
+
+#### 筛选选项格式
+```javascript
+{
+ teacherOptions: [
+ { id: 1, name: '张老师' }
+ ],
+ classroomOptions: [
+ { id: 1, name: '教室1' }
+ ],
+ classOptions: [
+ { id: 1, name: '花花-丁颖' }
+ ]
+}
+```
+
+### 3. 自定义配置
+
+#### 修改时间段
+```javascript
+// 在 data 中修改 timeSlots
+timeSlots: [
+ { time: '09:00', value: 9 },
+ { time: '10:00', value: 10 },
+ // ... 更多时间段
+]
+```
+
+#### 修改表格宽度
+```javascript
+// 调整表格总宽度(rpx)
+tableWidth: 1200
+```
+
+### 4. API 接口集成
+
+#### 获取课程数据
+```javascript
+async loadCourses() {
+ try {
+ const res = await apiRoute.getCourseSchedule({
+ startDate: this.currentWeekStart,
+ endDate: this.getWeekEndDate()
+ })
+ this.courses = res.data
+ } catch (error) {
+ console.error('加载课程失败:', error)
+ }
+}
+```
+
+#### 添加课程
+```javascript
+async addCourse(courseData) {
+ try {
+ const res = await apiRoute.addCourseSchedule(courseData)
+ if (res.code === 1) {
+ this.loadCourses() // 重新加载数据
+ }
+ } catch (error) {
+ console.error('添加课程失败:', error)
+ }
+}
+```
+
+## 性能优化建议
+
+### 1. 数据懒加载
+- 只加载当前周的数据
+- 切换周时再加载新数据
+
+### 2. 滚动优化
+- 使用 `scroll-view` 组件的惯性滚动
+- 避免在滚动事件中进行复杂计算
+
+### 3. 渲染优化
+- 使用 `v-show` 替代 `v-if` 进行显示隐藏
+- 合理使用 `key` 属性优化列表渲染
+
+## 扩展功能
+
+### 1. 课程详情
+```javascript
+// 点击课程查看详情
+handleCourseClick(course) {
+ this.$navigateTo({
+ url: `/pages/coach/course/info?id=${course.id}`
+ })
+}
+```
+
+### 2. 拖拽排课
+```javascript
+// 可以集成拖拽功能实现课程时间调整
+// 使用 movable-view 组件或手势事件
+```
+
+### 3. 批量操作
+```javascript
+// 支持批量选择和操作课程
+// 添加多选模式
+```
+
+## 注意事项
+
+1. **微信小程序兼容性**:使用了 `scroll-view` 组件,确保在微信小程序中正常滚动
+2. **数据更新**:切换日期时需要重新加载数据
+3. **内存管理**:大量数据时考虑虚拟滚动优化
+4. **网络状态**:处理网络异常情况,提供离线缓存
+
+## troubleshooting
+
+### 滚动不流畅
+- 检查 `scroll-view` 的高度设置
+- 减少滚动事件中的计算量
+
+### 数据不更新
+- 确保数据是响应式的
+- 检查 Vue 数据绑定是否正确
+
+### 样式错位
+- 检查 flex 布局设置
+- 确保单元格宽度一致
\ No newline at end of file
diff --git a/uniapp/pages/coach/schedule/schedule_table.vue b/uniapp/pages/coach/schedule/schedule_table.vue
new file mode 100644
index 00000000..1284882c
--- /dev/null
+++ b/uniapp/pages/coach/schedule/schedule_table.vue
@@ -0,0 +1,1036 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ formatDateRange() }}
+
+
+
+
+
+
+
+
+ 共 {{ totalCourses }} 节课,未点名 {{ unnamedCourses }} 节课
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ timeSlot.time }}
+
+
+
+
+
+ {{ course.courseName }}
+ {{ course.students }}
+ {{ course.teacher }}
+ {{ course.status }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 时间范围
+
+
+ {{ option.label }}
+
+
+
+
+
+
+ 授课老师
+
+
+ {{ teacher.name }}
+
+
+
+
+
+
+ 选择场地
+
+
+ {{ venue.name }}
+
+
+
+
+
+
+ 上课场地
+
+
+ {{ venue.name }}
+
+
+
+
+
+
+ 班级
+
+
+ {{ cls.name }}
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file