You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于所选日期获取可用预约时段的JS与API问题排查

解决预约时段筛选的TypeError问题

需求

实现用户按所选日期选择可用预约时段的功能,流程为:用户选择日期后弹出时段按钮,选中时段后展示表单剩余部分。

当前实现与问题

  • 已完成日期选择器转ISO格式、调用Shopmonkey API获取预约数据的代码
  • 实现了选日期后显示时段容器、点击时段按钮隐藏其他按钮的逻辑
  • 遍历API返回的预约数组筛选对应日期时段时,出现错误:T.js:40 TypeError: Cannot read properties of undefined (reading 'length') at GET.js:26:40

错误原因分析

报错核心是appointments.length中的appointments为undefined,具体原因:

  1. API请求未携带有效授权令牌(当前代码中authorization: 'Bearer '后为空),导致响应结构不符合预期,无data字段
  2. 未对API响应的response.data做存在性校验,直接访问length引发错误
  3. 日期筛选逻辑有误:用户选择的是无时分秒的日期,但请求API时用了带时分秒的ISO格式,无法正确获取当天所有预约

修复方案与优化代码

核心修复点

  1. 补充有效授权令牌到请求头
  2. 对API响应做安全校验,确保response.data存在后再遍历
  3. 调整API请求参数,获取所选日期当天的所有预约
  4. 优化时段筛选逻辑,自动禁用/隐藏已被预约的时段按钮

完整优化代码

JavaScript部分

// 日期转换、API请求与可用时段加载逻辑整合
function loadAvailableTimes() {
  const datePicker = document.getElementById('date-picker');
  const selectedDate = new Date(datePicker.value);
  if (!selectedDate || isNaN(selectedDate.getTime())) {
    console.error('请选择有效的日期');
    return;
  }

  // 构造所选日期的当天开始和次日开始时间,用于筛选当天所有预约
  const startOfDay = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate());
  const endOfDay = new Date(startOfDay.getTime() + 24 * 60 * 60 * 1000);
  const isoStart = startOfDay.toISOString();
  const isoEnd = endOfDay.toISOString();

  const options = {
    method: 'GET',
    headers: {
      accept: 'application/json',
      // 替换为你的有效Shopmonkey API令牌
      authorization: 'Bearer YOUR_SHOPMONKEY_API_TOKEN'
    }
  };

  fetch(`https://api.shopmonkey.io/v2/appointments?limit=19&offset=0&sort=startDate&from=${isoStart}&to=${isoEnd}`, options)
    .then(response => {
      if (!response.ok) throw new Error(`API请求失败:${response.status}`);
      return response.json();
    })
    .then(response => {
      // 安全获取预约数据,默认空数组
      const appointments = response.data || [];
      const timeButtons = document.querySelectorAll('.timeButton');

      // 遍历所有时段按钮,检查是否已被预约
      timeButtons.forEach(button => {
        button.style.display = 'inline-block'; // 先重置显示状态
        button.disabled = false;
        button.style.opacity = '1';
        button.style.cursor = 'pointer';

        const buttonTime = button.id.replace('T', ''); // 提取按钮时间,如"08:00"
        const [hour, minute] = buttonTime.split(':').map(Number);
        // 构造按钮对应的当天时间
        const buttonDateTime = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate(), hour, minute);

        // 检查该时段是否已被预约(假设预约时长30分钟,可按需调整)
        const isBooked = appointments.some(appt => {
          const apptStart = new Date(appt.startDate);
          const apptEnd = new Date(apptStart.getTime() + 30 * 60 * 1000);
          return buttonDateTime >= apptStart && buttonDateTime < apptEnd;
        });

        if (isBooked) {
          button.disabled = true;
          button.style.opacity = '0.5';
          button.style.cursor = 'not-allowed';
        }
      });

      // 显示时段容器
      document.getElementById('times-container').style.display = 'block';
    })
    .catch(err => console.error('加载预约时段失败:', err));
}

// 绑定日期选择变化事件
document.getElementById('date-picker').addEventListener('change', loadAvailableTimes);

// 点击时段按钮后隐藏其他按钮并展示表单
function selectTime(event) {
  const clickedButton = event.target;
  if (clickedButton.disabled) return;

  const allButtons = document.querySelectorAll('.timeButton');
  allButtons.forEach(button => {
    if (button !== clickedButton) {
      button.style.display = 'none';
    }
  });

  // 展示表单其余部分,替换为你的表单容器ID
  document.getElementById('booking-form').style.display = 'block';
}

// 绑定所有时段按钮的点击事件
document.querySelectorAll('.timeButton').forEach(button => {
  button.addEventListener('click', selectTime);
});

HTML时段按钮示例

<div id="times-container" style="display: none;">
  <button id="T08:00" class="timeButton">8:00am</button>
  <button id="T08:30" class="timeButton">8:30am</button>
  <!-- 其他时段按钮省略 -->
  <button id="T17:00" class="timeButton">5:00pm</button>
</div>

<div id="booking-form" style="display: none;">
  <!-- 表单其余内容 -->
</div>

补充说明

  • 请将代码中的YOUR_SHOPMONKEY_API_TOKEN替换为你的有效Shopmonkey API令牌
  • 预约时长(当前设为30分钟)请根据实际业务需求调整
  • 代码增加了无效日期、API请求失败等场景的错误处理,提升稳定性

内容的提问来源于stack exchange,提问作者sweeezyyy productions

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 22:47:05