Вопрос или проблема
Предыдущие бронирования определяются элементом кода php. Параметры eventOverlap и selectOverlap равны false, но у меня есть перекрытие событий.
document.addEventListener('DOMContentLoaded', function() {
var calendarEl = document.getElementById('calendar');
var reservations = [
<?php
foreach ($reservations as $index => $reservation) {
if ($index > 0) echo ',';
echo "{
start: '{$reservation['date_debut']}',
end: '{$reservation['date_fin']}',
backgroundColor: 'red',
rendering: 'background'
}";
}
?>
];
var calendar = new FullCalendar.Calendar(calendarEl, {
initialView: 'dayGridMonth',
selectable: true,
selectOverlap: function(event) {
return !event.rendering;
},
events: reservations,
eventOverlap: false,
selectOverlap: false,
select: function(info) {
var isConflict = reservations.some(function(reservation) {
var reservationStart = new Date(reservation.start).getTime();
//var reservationEnd = new Date(reservation.end).getTime() - 86400000; // Вычитать 1 день
var reservationEnd = new Date(reservation.end).getTime();
var selectedStart = new Date(info.startStr).getTime();
//var selectedEnd = new Date(info.endStr).getTime() - 86400000; // Вычитать 1 день
var selectedEnd = new Date(info.endStr).getTime();
return selectedStart <= reservationEnd && selectedEnd >= reservationStart;
});
if (isConflict) {
alert('Ошибка: выбранный период пересекается с существующим бронированием.');
calendar.unselect();
} else {
document.getElementById('date_debut').value = info.startStr;
// Корректное обновление даты окончания без дополнительного вычитания
document.getElementById('date_fin').value = info.endStr;
}
},
validRange: {
start: new Date().toISOString().split('T')[0] // Отключить выбор в прошлом
},
unselectAuto: false
});
calendar.render();
Не используйте eventOverlap: false или selectOverlap: false. Вместо этого обрабатывайте конфликты в функции saveevent, вызываемой функцией select.
//Эта функция срабатывает, когда в календаре нажимается свободный временной интервал
select: function(info) {
console.log('выбрано ' + info.startStr + ' до ' + info.endStr);
var options = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' };
var date = new Date(info.start);
// пример: Суббота, 17 сентября 2016
var formatteddate=date.toLocaleTimeString("en-US", options)
document.getElementById("formatteddate").value = formatteddate;
document.getElementById("hour").value = info.startStr;
saveevent();
}
saveevent
function saveevent(event) {
console.log("сохранить событие");
const user_id = JSON.parse(document.getElementById('user_id').textContent);
const servicelist = document.getElementById('servicelist').value;
const eventstart = document.getElementById("hour").value;
const totalminutes = document.getElementById("totalminutes").textContent;
console.log("час:" + eventstart );
fetch('/saveevent', {
method: 'PUT',
credentials:'include',
body: JSON.stringify({
userid: user_id,
eventstart:eventstart,
servicelist:servicelist,
totalminutes:totalminutes,
})
})
.then(response => response.json())
.then(result => {
// Печать результата
console.log(result);
if(result.message=="событие успешно сохранено.")
{
document.getElementById("errormessage").innerHTML="";
calendar.refetchEvents();
}
else
{
document.getElementById("errormessage").innerHTML=result.message;
}
});
};
функция python
def saveevent(request):
if request.method == "PUT":
# Получить содержимое события
data = json.loads(request.body)
startdate_str = data.get("eventstart", "")
servicelist=data.get("servicelist", "")
user = User.objects.get(id=request.user.id)
totalminutes=data.get("totalminutes", "")
start_date_obj = parse(startdate_str)
end_date_obj = start_date_obj + timedelta(minutes=int(totalminutes) )
print('вызов представления saveevent для пользователя с id: ' + str(request.user.id) + ' дата начала:' + str(startdate_str) + ' услуги ' + str(servicelist) )
now_obj=datetime.now(tz=timezone.utc)
earlieststart=now_obj + timedelta(hours=int(22) )
earlieststart_str = earlieststart.strftime('%A, %B %d в %I:%M %p')
if start_date_obj < earlieststart:
#Недостаточно предварительного уведомления. Самое раннее время начала - вторник, 17 ноября в 10:00.
return JsonResponse({"message": "Недостаточно предварительного уведомления. Самое раннее время начала - " + earlieststart_str }, status=201)
else:
#Конфликт возникает в 2 возможных сценариях:
#1. Предложенная дата начала попадает в промежуток времени существующего события
StartDateConflicts = Event.objects.filter(start_date__gt=startdate_str,start_date__lt=end_date_obj)
#2. Предложенная дата окончания попадает в промежуток времени существующего события
EndDateConflicts = Event.objects.filter(end_date__gt=startdate_str,end_date__lt=end_date_obj)
conflictingevents = StartDateConflicts | EndDateConflicts
if conflictingevents:
return JsonResponse({"message": "событие конфликтует с " + str(conflictingevents.count()) + " другими событиями в расписании"}, status=201)
else:
#datetime.strptime(startdate_str, '%Y-%m-%d %H:%M:%S.%f')
#удалить все неподтвержденные события для пользователя
Event.objects.filter(owner_id=user.id, confirmed=False).delete()
event = Event(
event_name=user.username,
start_date=start_date_obj,
end_date=end_date_obj,
owner_id=user.id,
create_date=now_obj,
)
event.save()
print('событие сохранено:' + user.username + ' ' + startdate_str)
return JsonResponse({"message": "событие успешно сохранено."}, status=201)
else:
return JsonResponse({"message": "Неверный метод запроса. Ожидался метод POST."}, status=400)
Ответ или решение
Предотвращение наложения периодов бронирования с использованием FullCalendar
Ваша задача — предотвратить наложение периодов бронирования при использовании FullCalendar для управления событиями бронирования. Давайте рассмотрим, как это можно реализовать шаг за шагом, устраняя существующие проблемы и улучшая поведение вашего приложения.
1. Проблема наложения событий
Ваша текущая реализация использует следующие параметры:
eventOverlap: false,
selectOverlap: false,
Однако они не предотвращают наложение событий, поскольку ваша реализация обработки выборки событий не учитывает существующие события. Вместо того чтобы использовать некоторые пропущенные параметры, вы можете воспользоваться подходом обработки конфликтов в select
функции.
2. Обработка конфликтов в функции select
Измените функцию select
, чтобы она проверяла наличие конфликтов с существующими бронированиями, а затем принимала решение о выборе:
select: function(info) {
var isConflict = reservations.some(function(reservation) {
var reservationStart = new Date(reservation.start).getTime();
var reservationEnd = new Date(reservation.end).getTime();
var selectedStart = new Date(info.startStr).getTime();
var selectedEnd = new Date(info.endStr).getTime();
return selectedStart < reservationEnd && selectedEnd > reservationStart;
});
if (isConflict) {
alert('Ошибка: выбранный период наложивается на существующее бронирование.');
calendar.unselect();
} else {
document.getElementById('date_debut').value = info.startStr;
document.getElementById('date_fin').value = info.endStr;
saveevent(); // сериализуйте событие, если выбор успешен
}
}
Этот код проверяет, пересекается ли выбранный период с любым из существующих бронирований. Если конфликт обнаруживается, пользователю показывается сообщение об ошибке, и выделение отменяется.
3. Рендеринг и управление событиями
Обратите особое внимание на массив reservations
, в который должны были попасть все существующие бронирования. Убедитесь, что данные формируются корректно через PHP.
Пример PHP скрипта для создания событий
Убедитесь, что ваши события правильно формируются в массиве JavaScript:
var reservations = [
<?php foreach ($reservations as $index => $reservation): ?>
{
start: '<?php echo $reservation['date_debut']; ?>',
end: '<?php echo $reservation['date_fin']; ?>',
backgroundColor: 'red',
rendering: 'background'
}<?php if ($index < count($reservations) - 1) echo ','; ?>
<?php endforeach; ?>
];
4. Проверка на серверной стороне
Несмотря на проверки на клиенте, рекомендуется дублировать проверку на серверной стороне. Ваш метод saveevent
в Python выполняет проверку пересечений, и это важно для обеспечения целостности данных:
StartDateConflicts = Event.objects.filter(start_date__lt=end_date_obj, end_date__gt=start_date_obj)
Этот запрос полезен, так как проверяет пересечения на самом языке базы данных, что делает вашу систему более надежной.
5. Вывод
Теперь, когда вы внедрили проверки как на клиенте, так и на сервере, ваше приложение должно корректно обрабатывать ситуации наложения событий. Это улучшит пользовательский опыт, а также избежать проблем с пересечениями, которые могут привести к недоразумениям при бронировании.
Надеюсь, этот процесс поможет вам быстро устранить проблему и сделать вашу систему бронирования более эффективной и надежной. Если у вас возникнут дополнительные вопросы или потребуется больше примеров, пожалуйста, не стесняйтесь обращаться за помощью.