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

如何仅针对特定日期从下拉菜单中移除预订时段?

按日期过滤已预订时段的实现方案

要实现选中日期的已预订时段自动隐藏,其他日期不受影响,需要完成以下几个核心步骤:


1. 新增状态存储已预订记录

在组件中添加状态,保存所有已提交的预订信息:

const [bookedSlots, setBookedSlots] = useState([]);

2. 实时获取并监听已预订数据

修改useEffect,同时获取可用时段和已预订记录,并且通过onSnapshot监听Firestore的实时变化,确保页面能及时更新:

useEffect(() => {
  // 获取基础可用时段
  const fetchTimes = async () => {
    const timeData = await getDocs(timesRef);
    setTimes(timeData.docs.map(doc => ({ ...doc.data(), id: doc.id })));
  };

  // 监听预订记录的实时更新
  const unsubscribe = onSnapshot(formRef, (snapshot) => {
    const bookedData = snapshot.docs.map(doc => ({
      date: doc.data().date.toDate(), // 转换为JS Date对象方便对比
      time: doc.data().time
    }));
    setBookedSlots(bookedData);
  });

  fetchTimes();
  return unsubscribe; // 组件卸载时取消监听,避免内存泄漏
}, []);

3. 根据选中日期过滤可用时段

创建计算属性,对比当前选中日期和已预订记录,过滤掉该日期已被占用的时段:

const availableTimes = date 
  ? times.filter(timeItem => {
      // 检查当前时段是否在选中日期的已预订列表中
      const isBooked = bookedSlots.some(slot => 
        slot.date.toDateString() === date.toDateString() && slot.time === timeItem.time
      );
      return !isBooked;
    })
  : [];

4. 更新下拉菜单的渲染逻辑

根据计算出的availableTimes渲染下拉选项,同时处理未选日期和无可用时段的情况:

<div className="pt-10">
  {date ? (
    <select onChange={(e) => setTime(e.target.value)}>
      {availableTimes.length > 0 ? (
        availableTimes.map((item) => (
          <option key={item.id} value={item.time}>
            {item.time}
          </option>
        ))
      ) : (
        <option value="">该日期暂无可用时段</option>
      )}
    </select>
  ) : (
    <p className="text-gray-500">请先选择日期</p>
  )}
</div>

5. 优化日期存储格式(可选)

提交预订时,将JS Date对象转换为Firestore的Timestamp类型,保证存储格式统一:

const createBooking = async () => {
  await addDoc(formRef, {
    name: name,
    date: Timestamp.fromDate(date),
    email: email,
    number: number,
    team: team,
    age: age,
    time: time,
  });
};

完整修改后的组件代码

function GThirtyminsp() {
  // Form values
  const [date, setDate] = useState(null);
  const [email, setEmail] = useState("");
  const [name, setName] = useState("");
  const [age, setAge] = useState("");
  const [team, setTeam] = useState("");
  const [number, setNumber] = useState("");
  const [time, setTime] = useState(null);

  // To show times on screen
  const [times, setTimes] = useState([]);
  // 存储已预订的时段信息
  const [bookedSlots, setBookedSlots] = useState([]);

  const timesRef = collection(db, "30min-goalie-semiprivate-times");
  const formRef = collection(db, "30min-goalie-semiprivate");

  useEffect(() => {
    const fetchTimes = async () => {
      const timeData = await getDocs(timesRef);
      setTimes(timeData.docs.map(doc => ({ ...doc.data(), id: doc.id })));
    };

    // 实时监听预订记录变化
    const unsubscribe = onSnapshot(formRef, (snapshot) => {
      const bookedData = snapshot.docs.map(doc => ({
        date: doc.data().date.toDate(),
        time: doc.data().time
      }));
      setBookedSlots(bookedData);
    });

    fetchTimes();
    return unsubscribe;
  }, []);

  // 计算当前选中日期的可用时段
  const availableTimes = date 
    ? times.filter(timeItem => {
        const isBooked = bookedSlots.some(slot => 
          slot.date.toDateString() === date.toDateString() && slot.time === timeItem.time
        );
        return !isBooked;
      })
    : [];

  const createBooking = async () => {
    await addDoc(formRef, {
      name: name,
      date: Timestamp.fromDate(date),
      email: email,
      number: number,
      team: team,
      age: age,
      time: time,
    });
  };

  const handleSubmit = (e) => {
    e.preventDefault();

    createBooking();

    // 重置表单
    setName("");
    setEmail("");
    setDate(null);
    setNumber("");
    setTeam("");
    setTime(null);
    setAge("");
  };

  return (
    <div>
      <h1 className="text-3xl w-full text-center pt-10 font-bold">
        Book 30min Semi-Private Goalie Clinic
      </h1>
      <div className="flex flex-col items-center">
        <form onSubmit={handleSubmit} className="flex flex-col pt-10 w-3/4">
          <input
            type="text"
            name="name"
            placeholder="Name"
            value={name}
            onChange={(e) => setName(e.target.value)}
            className="border-2 p-3 mb-5 border-gray rounded-lg"
          />
          <input
            type="text"
            name="email"
            placeholder="Email"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
            className="border-2 p-3 mb-5 border-gray rounded-lg"
          />
          <input
            type="text"
            name="number"
            placeholder="Phone Number"
            value={number}
            onChange={(e) => setNumber(e.target.value)}
            className="border-2 p-3 mb-5 border-gray rounded-lg"
          />
          <input
            type="text"
            name="team"
            placeholder="Team Name"
            value={team}
            onChange={(e) => setTeam(e.target.value)}
            className="border-2 p-3 mb-5 border-gray rounded-lg"
          />
          <input
            type="text"
            name="age"
            placeholder="Age"
            value={age}
            onChange={(e) => setAge(e.target.value)}
            className="border-2 p-3 mb-5 border-gray rounded-lg"
          />
          <h2 className="text-center text-xl py-5 font-bold">
            Pick a Date and Time
          </h2>
          <div className="pb-10">
            <DatePicker
              className="border-2 p-3 w-full border-gray rounded-lg"
              selected={date}
              onChange={(date) => setDate(date)}
              placeholderText="Select a Date"
            />
            <div className="pt-10">
              {date ? (
                <select onChange={(e) => setTime(e.target.value)}>
                  {availableTimes.length > 0 ? (
                    availableTimes.map((item) => (
                      <option key={item.id} value={item.time}>
                        {item.time}
                      </option>
                    ))
                  ) : (
                    <option value="">该日期暂无可用时段</option>
                  )}
                </select>
              ) : (
                <p className="text-gray-500">请先选择日期</p>
              )}
            </div>
          </div>

          <button
            type="submit"
            className="bg-accent text-white font-semibold p-3 rounded-full"
          >
            Submit
          </button>
        </form>
      </div>
    </div>
  );
}

关键细节说明

  • 使用onSnapshot实现实时更新,当有新的预订提交时,页面会自动刷新可用时段
  • 通过toDateString()对比日期,只匹配年月日,忽略时间部分,确保日期判断准确
  • 未选择日期时显示提示文本,避免下拉菜单为空的尴尬情况
  • 提交时转换日期为Timestamp,保证Firestore存储格式一致,避免后续读取时的格式问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 20:05:13