教培管家第13讲:课程排课——智能排课算法与自动化生成

目录

  • 前言
  • 第一步:智能批量排课逻辑实现
    • [1.1 搭建动态时段列表](#1.1 搭建动态时段列表)
    • [1.2 周期预览与保存区](#1.2 周期预览与保存区)
  • 第二步:核心交互逻辑
    • [2.1 计算总课时 (`calcTotalSessions`)](#2.1 计算总课时 (calcTotalSessions))
    • [2.2. 添加排课时段 (`addScheduleSlot`)](#2.2. 添加排课时段 (addScheduleSlot))
    • [2.3 更新时段属性 (`updateScheduleSlot`)](#2.3 更新时段属性 (updateScheduleSlot))
    • [2.4 删除时段(`deleteScheduleSlot`)](#2.4 删除时段(deleteScheduleSlot))
    • [2.5. 执行自动化排课 (`executeBatchScheduling`)](#2.5. 执行自动化排课 (executeBatchScheduling))
  • 第三步:设置组件交互
  • 第四步:功能验证
  • [🌟 总结与预告](#🌟 总结与预告)

前言

在本讲的上半部分,我们完成了排课模型的建立、弹窗视图的初始化以及"周卡片"的切换交互。本篇我们将攻克最核心的两个环节:动态时段配置自动化排课算法


第一步:智能批量排课逻辑实现

1.1 搭建动态时段列表

在用户点击某个"周卡片"后,中部区域需要展示该天已设定的所有时段。

先添加一个按钮,内容改为添加时段

继续添加循环展示组件

数据绑定 :数据源绑定为当前选中天的规则数组:
$w.page.dataset.state.scheduleRules[$w.page.dataset.state.currentDay] || []

内容设计:在循环项中放入"容器",并横向排列以下组件:

  • 时间选择器:绑定开始时间。
  • 时间选择器:绑定结束时间。
  • 下拉选择 :绑定执教老师(数据源关联 Employee)。
  • 下拉选择 :绑定上课教室(数据源关联 Classroom)。
  • 图标按钮:用于删除当前时段。

⚠️ 注意 :由于微搭的循环项中无法直接实现"双向绑定",我们需要通过 onChange 事件调用 JS 方法来同步数据。


1.2 周期预览与保存区

在弹窗底部,我们需要展示最终的统计信息并触发保存。

总课时预览 :添加文本组件,绑定公式计算整个周期内会生成的课节总数。

创建一个变量用来显示我们预期的排课总时长

然后给文本绑定表达式显示时长


第二步:核心交互逻辑

为了驱动复杂的排课交互,我们需要在"应用属性 -> 自定义方法"中编写以下核心逻辑。

2.1 计算总课时 (calcTotalSessions)

为了保证实时预览,我们需要一个通用的计算方法,在日期范围或排课规则变化时调用。

javascript 复制代码
/**
 * 根据日期范围和排课规则计算总课时
 */
export default async function({event, data}) {
  const { startDate, endDate, scheduleRules } = $w.page.dataset.state;
  if (!startDate || !endDate || !scheduleRules) {
    $w.page.dataset.state.totalSessions = 0;
    return;
  }
  
  let total = 0;
  let curr = new Date(startDate);
  const end = new Date(endDate);
  
  while (curr <= end) {
    const day = curr.getDay() === 0 ? 7 : curr.getDay();
    total += (scheduleRules[day]?.length || 0);
    curr.setDate(curr.getDate() + 1);
  }
  
  $w.page.dataset.state.totalSessions = total;
}

2.2. 添加排课时段 (addScheduleSlot)

点击"+ 添加时段"时,为当前选中的星期初始化数据并追加默认值。注意时间戳的处理:

javascript 复制代码
/**
 * 为当前选中的天新增一个排课时段
 */
export default function({event, data}) {
 const currentDay = $w.page.dataset.state.currentDay;
  const scheduleRules = $w.page.dataset.state.scheduleRules || {};
  
  if (!scheduleRules[currentDay]) {
    scheduleRules[currentDay] = [];
  }
  
  const { teacherId, classroomId,courseType } = $w.page.dataset.state.classInfo;
  console.log(teacherId,classroomId)
  const defaultStart = 9 * 60 * 60 * 1000; // 09:00
  const defaultEnd = 10.5 * 60 * 60 * 1000; // 10:30

  scheduleRules[currentDay].push({
    id: Date.now(),
    startTime: defaultStart,
    endTime: defaultEnd,
    teacherId: teacherId?._id||"",
    classroomId: classroomId?._id||"",
    courseType:courseType|| '2'
  });
  
  $w.page.dataset.state.scheduleRules = { ...scheduleRules };
}

2.3 更新时段属性 (updateScheduleSlot)

当用户通过时间选择器修改时间时,微搭组件会返回该日期的时间戳(Number),我们直接同步即可:

javascript 复制代码
/**
 * 更新指定时段的字段值(支持时间戳)
 */
export default function({event, data}) {
const { field, value, index } = data.target; 
  const currentDay = $w.page.dataset.state.currentDay;
  const rules = $w.page.dataset.state.scheduleRules;
  
  // 更新对应位置的属性
  rules[currentDay][index][field] = value;
  
  // 必须通过重新赋值触发状态变量的响应式更新
  $w.page.dataset.state.scheduleRules = { ...rules };
}

2.4 删除时段(deleteScheduleSlot

点击时段行末尾的删除图标时,移除当前选中的时段:

bash 复制代码
export default function({event, data}) {
const { index } = data.target;
  const currentDay = $w.page.dataset.state.currentDay;
  const rules = $w.page.dataset.state.scheduleRules;
  
  // 从数组中移除
  rules[currentDay].splice(index, 1);
  
  // 重新赋值触发 UI 刷新
  $w.page.dataset.state.scheduleRules = { ...rules };
  
  $w.utils.showToast({ title: '已移除该时段', icon: 'success' });
}

2.5. 执行自动化排课 (executeBatchScheduling)

在处理时间戳时,我们需要将"日期"的时间戳与"时段"的时间戳(提取时分秒)进行合并计算:

javascript 复制代码
export default async function ({ event, data }) {
  try {
    const scheduleRules = $w.page.dataset.state.scheduleRules;
    const startDateMs = $w.date1.value;   // 毫秒值,代表那天 00:00:00
    const endDateMs   = $w.date2.value;   // 毫秒值,代表那天 00:00:00
    const classInfo   = $w.page.dataset.state.classInfo;

    if (!startDateMs || !endDateMs) {
      throw new Error('请选择排课周期');
    }

    $w.utils.showLoading({ title: '正在生成课表...' });

    const sessions = [];
    let currentMs = startDateMs;

    // 建议:把 endDate 当天也包含进来(≤)
    while (currentMs <= endDateMs) {
      // 获取星期几(0=周日 → 7)
      const dateObj = new Date(currentMs);
      const dayOfWeek = dateObj.getDay() === 0 ? 7 : dateObj.getDay();

      const dayRules = scheduleRules[dayOfWeek];

      if (dayRules && Array.isArray(dayRules) && dayRules.length > 0) {
        for (const rule of dayRules) {
          // 直接使用 rule.startTime / endTime(一天内的毫秒偏移)
          const finalStart =  rule.startTime;
          const finalEnd   = rule.endTime;

          sessions.push({
            classId:    { _id: classInfo._id },
            date:       currentMs,           // 当天 00:00 的毫秒值(日期基准)
            startTime:  finalStart,          // 完整开始时间戳
            endTime:    finalEnd,            // 完整结束时间戳
            teacherId:  { _id: rule.teacherId },
            classroomId:{ _id: rule.classroomId },
            courseType: rule.courseType,
            status:     '1'
          });
        }
      }

      // +1 天(86400000 ms)
      currentMs += 86400000;
    }

    const result = await $w.cloud.callDataSource({
      dataSourceName: 'Schedule',
      methodName:    'wedaBatchCreateV2',
      params:        { data: sessions }
    });

    $w.utils.hideLoading();

    $w.utils.showModal({
      title:      '排课成功',
      content:    `已成功生成 ${result.idList?.length || 0} 节课。`,
      showCancel: false
    });

    $w.modal1.close({});
    $w.table1.refresh();

  } catch (e) {
    $w.utils.hideLoading();
    $w.utils.showToast({
      title: e.message || '生成课表失败',
      icon:  'error'
    });
  }
}

第三步:设置组件交互

方法定义好之后,就可以给组件配置事件了。

首先给添加时段配置点击事件,先调用添加方法,接着调用计算课时

然后给删除图标定义点击事件,调用删除方法,跟一个重新计算课时的方法

给表格里的每个组件配置值改变事件,调用我们的更新方法

给弹窗的确认按钮添加点击事件,调用我们的批量排课方法

第四步:功能验证

  1. 时段动态性:点击"周三",添加两个时段,然后切换到"周五"查看是否为空,再切回"周三"确认数据是否保留。
  2. 默认回填:确认添加新时段时,老师和教室是否自动回填为该班级的默认配置。
  3. 生成结果:选择一个月的时间范围,点击保存,进入"排课管理"页面查看生成的记录是否准确对应周三的所有时段。




🌟 总结与预告

通过这两篇的学习,我们实现了一个极具专业感的"智能排课向导"。这不仅是简单的增删改查,更是将**业务逻辑(排课规则)转化为生产数据(课节记录)**的典型案例。

下一讲: 《连接家校------家长端注册、登录与身份绑定》。我们将进入小程序的开发,学习如何通过手机号验证码实现家长的快速注册,并完成学员信息的关联绑定。

相关推荐
Java小卷1 天前
流程设计器为啥选择diagram-js
前端·低代码·工作流引擎
一枚前端小姐姐3 天前
低代码平台表单设计系统技术分析(实战三)
前端·vue.js·低代码
一枚前端小姐姐3 天前
低代码平台表单设计系统技术分析(实战二)
低代码·架构·前端框架
一枚前端小姐姐3 天前
低代码平台表单设计系统架构分析(实战一)
前端·低代码·架构
麦聪聊数据4 天前
统一 Web SQL 平台如何收编企业内部的“野生数据看板”?
数据库·sql·低代码·微服务·架构
吴声子夜歌4 天前
小程序——布局示例
小程序
luffy54594 天前
微信小程序页面使用类似filter函数的wxs语法
微信小程序·小程序
Slow菜鸟4 天前
微信小程序开发(二)目录结构完全指南
微信小程序·小程序
小小王app小程序开发4 天前
海外盲盒小程序抽赏玩法分析(附跨境技术落地要点)
小程序