基于所选日期获取可用预约时段的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,具体原因:
- API请求未携带有效授权令牌(当前代码中
authorization: 'Bearer '后为空),导致响应结构不符合预期,无data字段 - 未对API响应的
response.data做存在性校验,直接访问length引发错误 - 日期筛选逻辑有误:用户选择的是无时分秒的日期,但请求API时用了带时分秒的ISO格式,无法正确获取当天所有预约
修复方案与优化代码
核心修复点
- 补充有效授权令牌到请求头
- 对API响应做安全校验,确保
response.data存在后再遍历 - 调整API请求参数,获取所选日期当天的所有预约
- 优化时段筛选逻辑,自动禁用/隐藏已被预约的时段按钮
完整优化代码
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
相关产品推荐
相关产品推荐

