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

如何从Firebase Firestore获取指定日期已预约时段并过滤可选时段

预约系统时段过滤问题

我正在开发一个预约系统,用户提交的信息以日期作为文档ID存储在Firebase Firestore中。需求是获取指定日期已被选择的时段,将这些时段从当前用户的可选时段中过滤掉,避免不同用户在同一日期重复预约。

之前的代码会过滤掉所有日期的已选时段,而我只需要过滤当前选定日期的已预约时段。

Firestore数据结构

reservations集合的文档ID为日期格式(如2024-05-20),每个文档包含一个reservations数组,数组元素是包含time、name等字段的预约对象。

之前的错误代码

async function fetchBookedTimes() {
  const bookedTimesRef = collection(db, 'reservations')
  const querySnapshot = await getDocs(bookedTimesRef)
  const bookedTimesArray = []
  querySnapshot.forEach((doc) => {
    const data = doc.data()
    bookedTimesArray.push(...data.reservations.map((reservation) => reservation.time))
  })
  bookedTimes.value = bookedTimesArray
}

watch(date, async () => {
  await fetchBookedTimes()
})

const availableTimes = computed(() => {
  const times = []
  for (let hour = 8; hour <= 22; hour++) {
    for (let minute = 0; minute <= 30; minute += 30) {
      times.push(`${hour}:${minute.toString().padStart(2, '0')}`)
    }
  }
  return times.filter((time) => !bookedTimes.value.includes(time))
})

现有代码

模板部分

<div class="form-group">
  <label>Date(Month/Day/Year):</label>
  <input type="date" v-model="date" :min="currentDate" name="Date" required />
</div>

<div class="form-group">
  <label>Time:</label>
  <select v-model="time" name="Time" class="time-sec" required>
    <option value="" disabled hidden>Select The time</option>
    <option v-for="time in availableTimes" :key="time" :value="time">
      {{ time }}
    </option>
  </select>
</div>

脚本部分

import { ref } from "vue"

const date = ref('')
const time = ref('')
const bookedTimes = ref([])

const reservations = ref({})
const docId = date.value
const currentDate = ref(new Date().toISOString().slice(0, 10))

async function fetchBookedTimes() {
  const bookedTimesRef = doc(db, 'reservations', docId)
  const docSnapshot = await getDoc(bookedTimesRef)
  const bookedTimesArray = []
  if (docSnapshot.exists()) {
    const data = docSnapshot.data()
    bookedTimesArray.push(...data.reservations.map((reservations) => reservations.time))
  }
  bookedTimes.value = bookedTimesArray
  console.log(bookedTimesArray)
}

watch(date, async () => {
  await fetchBookedTimes()
})

const availableTimes = computed(() => {
  const times = []
  for (let hour = 8; hour <= 22; hour++) {
    for (let minute = 0; minute <= 30; minute += 30) {
      times.push(`${hour}:${minute.toString().padStart(2, '0')}`)
    }
  }
  return times.filter((time) => !bookedTimes.value.includes(time))
})

async function handleSubmit(event) {
 bookedTimes.value.push({ date: date.value, time: time.value })
  // Check if all times for the selected date are booked
  if (availableTimes.value.length === 0) {
    // Move to the next available date
    date.value = new Date(date.value).addDays(1).toISOString().split('T')[0]
    // Reset booked times for the new date
    bookedTimes.value = bookedTimes.value.filter((booking) => booking.date !== date.value)
  }

  // Create a new reservation object
  const reservationData = {
    time: time.value,
    name: name.value,
    email: email.value,
    phone: phone.value
  }

  // Add the reservation to the reservations object
  if (!reservations.value[date.value]) {
    reservations.value[date.value] = []
  }
  reservations.value[date.value].push(reservationData)

  // Log the reservations
  console.log('Reservations:', reservations.value)

const dateId = date.value
  const reservationRef = doc(collection(db, 'reservations'), dateId)

  try {
    getDoc(reservationRef).then((doc) => {
      if (doc.exists) {
        // Update the existing document
        updateDoc(reservationRef, {
          reservations: [...doc.data().reservations, reservationData]
        })
      } else {
        // Create a new document
        setDoc(reservationRef, {
          reservations: [reservationData]
        })
      }
    })

  } catch (error) {
    console.error(error)
  }
}

问题修复方案

现有代码核心问题:docId初始化时取了date.value的初始空值,后续日期变化时docId不会自动更新,导致请求无效文档;同时handleSubmit中bookedTimes的更新逻辑格式不统一。

修复后的完整脚本代码

import { ref, watch, computed } from "vue"

const date = ref('')
const time = ref('')
const bookedTimes = ref([])
const name = ref('')
const email = ref('')
const phone = ref('')
const currentDate = ref(new Date().toISOString().slice(0, 10))

// 补充日期递增方法
Date.prototype.addDays = function(days) {
  const date = new Date(this.valueOf())
  date.setDate(date.getDate() + days)
  return date
}

async function fetchBookedTimes() {
  if (!date.value) return
  const bookedTimesRef = doc(db, 'reservations', date.value)
  const docSnapshot = await getDoc(bookedTimesRef)
  const bookedTimesArray = []
  if (docSnapshot.exists()) {
    const data = docSnapshot.data()
    bookedTimesArray.push(...data.reservations.map(reservation => reservation.time))
  }
  bookedTimes.value = bookedTimesArray
}

watch(date, async () => {
  await fetchBookedTimes()
})

const availableTimes = computed(() => {
  const times = []
  for (let hour = 8; hour <= 22; hour++) {
    for (let minute = 0; minute <= 30; minute += 30) {
      times.push(`${hour}:${minute.toString().padStart(2, '0')}`)
    }
  }
  return times.filter(time => !bookedTimes.value.includes(time))
})

async function handleSubmit(event) {
  event.preventDefault()
  if (!date.value || !time.value) return

  // 统一格式更新本地已选时段
  if (!bookedTimes.value.includes(time.value)) {
    bookedTimes.value.push(time.value)
  }

  // 检查日期是否约满并切换
  if (availableTimes.value.length === 0) {
    date.value = new Date(date.value).addDays(1).toISOString().split('T')[0]
    await fetchBookedTimes()
    time.value = ''
  }

  // 提交到Firestore
  const reservationData = {
    time: time.value,
    name: name.value,
    email: email.value,
    phone: phone.value
  }

  const reservationRef = doc(db, 'reservations', date.value)
  try {
    const docSnapshot = await getDoc(reservationRef)
    if (docSnapshot.exists()) {
      await updateDoc(reservationRef, {
        reservations: [...docSnapshot.data().reservations, reservationData]
      })
    } else {
      await setDoc(reservationRef, {
        reservations: [reservationData]
      })
    }
    // 重置表单
    time.value = ''
    name.value = ''
    email.value = ''
    phone.value = ''
  } catch (error) {
    console.error('提交失败:', error)
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 04:52:02