我遇到了日历问题。我使用 FullCalendar 来选择日期范围。例如,当我选择 11/20/23 到 11/22/23 时。
全日历又增加了 1 天(参见屏幕)。
这是我选择20到22时的结果。
我不明白问题从何而来。
这是我已经制作的代码。
var CalendarProduct = function () {
var calendarUnvailable;
var data = {
id: '',
eventName: '',
startDate: '',
endDate: '',
allDay: false
};
// Add event variables
var eventName;
var eventDescription;
var eventLocation;
var startDatepicker;
var startFlatpickr;
var endDatepicker;
var endFlatpickr;
var startTimepicker;
var startTimeFlatpickr;
var endTimepicker
var endTimeFlatpickr;
var modal;
var modalTitle;
var form;
var validator;
var addButton;
var submitButton;
var cancelButton;
var closeButton;
var viewEventName;
var viewAllDay;
var viewEventDescription;
var viewEventLocation;
var viewStartDate;
var viewEndDate;
var viewModal;
var viewEditButton;
var viewDeleteButton;
var initCalendarApp = function () {
moment.locale('fr');
let calendarElt = document.getElementById('date_unvailable');
var todayDate = moment().startOf('day');
var TODAY = todayDate.format('YYYY-MM-DD');
calendarUnvailable = new FullCalendar.Calendar(calendarElt, {
plugins: [dayGridPlugin, timeGridPlugin, listPlugin, interactionPlugin],
events: JSON.parse(unvailable),
initialView: 'dayGridWeek',
headerToolbar: {
left: 'prev,next today',
center: 'title',
right: 'dayGridMonth,timeGridWeek,timeGridDay'
},
locales: [frLocale],
locale: 'fr',
initialDate: TODAY,
themeSystem: 'bootstrap',
navLinks: true,
selectable: true,
selectMirror: true,
dayHeaderContent: (args) => {
return moment(args.date).format('dddd')
},
eventResize: function (info) {
console.log(info);
},
// Select dates action --- more info: https://fullcalendar.io/docs/select-callback
select: function (arg) {
arg.allDay = false;
formatArgs(arg);
handleNewEvent();
},
eventDrop: function (info) {
$.post(calendarUnvailableEditRoute, {
'datas': {
title: info.event.title,
id: info.event.id,
start: info.event.start.toISOString(),
end: info.event.end.toISOString()
}
})
.done(function () {
Swal.fire(
"Enregistré !",
"Date déplacée",
"success"
)
})
.fail(function () {
Swal.fire(
"Internal Error",
"Votre date n'a pas été déplacée, contacter un administrateur",
"error"
)
})
},
eventClick: function (arg) {
formatArgs({
id: arg.event.id,
title: arg.event.title,
startStr: arg.event.start,
endStr: arg.event.end,
allDay: arg.event.allDay
});
handleViewEvent();
},
editable: true,
dayMaxEvents: true
});
calendarUnvailable.render();
}
const handleNewEvent = () => {
// Update modal title
modalTitle.innerText = "Ajouter une indisponibilité";
modal.show();
// Select datepicker wrapper elements
const datepickerWrappers = form.querySelectorAll('[data-kt-calendar="datepicker"]');
// Handle all day toggle
const allDayToggle = form.querySelector('#kt_calendar_datepicker_allday');
allDayToggle.addEventListener('click', e => {
if (e.target.checked) {
datepickerWrappers.forEach(dw => {
dw.classList.add('d-none');
});
} else {
endFlatpickr.setDate(data.endDate, true, 'Y-m-d H:i');
datepickerWrappers.forEach(dw => {
dw.classList.remove('d-none');
});
}
});
populateForm(data);
// Handle submit form
submitButton.addEventListener('click', function (e) {
// Prevent default button action
e.preventDefault();
// Validate form before submit
if (validator) {
validator.validate().then(function (status) {
if (status == 'Valid') {
// Show loading indication
submitButton.setAttribute('data-kt-indicator', 'on');
// Disable submit button whilst loading
submitButton.disabled = true;
// Simulate form submission
setTimeout(function () {
// Simulate form submission
submitButton.removeAttribute('data-kt-indicator');
// Show popup confirmation
Swal.fire({
text: "Nouvelle disponibilité ajouté avec succès !",
icon: "success",
buttonsStyling: false,
confirmButtonText: "D'accord, j'ai compris !",
customClass: {
confirmButton: "btn btn-primary"
}
}).then(function (result) {
if (result.isConfirmed) {
modal.hide();
// Enable submit button after loading
submitButton.disabled = false;
// Detect if is all day event
let allDayEvent = false;
if (allDayToggle.checked) {
allDayEvent = true;
}
if (startTimeFlatpickr.selectedDates.length === 0) {
allDayEvent = true;
}
// Merge date & time
var startDateTime = moment(startFlatpickr.selectedDates[0]).format();
var endDateTime = moment(endFlatpickr.selectedDates[endFlatpickr.selectedDates.length - 1]).format();
if (!allDayEvent) {
const startDate = moment(startFlatpickr.selectedDates[0]).format('YYYY-MM-DD');
const endDate = startDate;
const startTime = moment(startTimeFlatpickr.selectedDates[0]).format('HH:mm:ss');
const endTime = moment(endTimeFlatpickr.selectedDates[0]).format('HH:mm:ss');
startDateTime = startDate + 'T' + startTime;
endDateTime = endDate + 'T' + endTime;
}
// Add new event to calendar
calendarUnvailable.addEvent({
id: uid(),
title: eventName.value,
start: startDateTime,
end: endDateTime,
className: 'fc-event-success',
allDay: allDayEvent
});
calendarUnvailable.render();
// Save in database
$.post(calendarUnvailableAddRoute, {
'datas': {
title: eventName.value,
start: startDateTime,
end: endDateTime,
allDay: allDayEvent
}
})
.fail(function () {
Swal.fire(
"Internal Error",
"Votre disponibilité n'a pas été enregistré, contacter un administrateur",
"error"
)
})
// Reset form for demo purposes only
form.reset();
}
});
//form.submit(); // Submit form
}, 2000);
} else {
// Show popup warning
Swal.fire({
text: "Désolé, il semble qu'il y ait des erreurs détectées, veuillez réessayer.",
icon: "error",
buttonsStyling: false,
confirmButtonText: "D'accord, j'ai compris !",
customClass: {
confirmButton: "btn btn-primary"
}
});
}
});
}
});
}
const initDatepickers = () => {
startFlatpickr = flatpickr(startDatepicker, {
enableTime: false,
dateFormat: "d/m/Y",
locale: French,
static: true
});
endFlatpickr = flatpickr(endDatepicker, {
enableTime: false,
dateFormat: "d/m/Y",
locale: French,
static: true
});
startTimeFlatpickr = flatpickr(startTimepicker, {
enableTime: true,
noCalendar: true,
dateFormat: "H:i",
locale: French,
static: true
});
endTimeFlatpickr = flatpickr(endTimepicker, {
enableTime: true,
noCalendar: true,
dateFormat: "H:i",
locale: French,
static: true
});
}
// Init validator
const initValidator = () => {
// Init form validation rules. For more info check the FormValidation plugin's official documentation:https://formvalidation.io/
validator = FormValidation.formValidation(
form,
{
fields: {
'calendar_event_name': {
validators: {
notEmpty: {
message: 'Le titre est requis'
}
}
},
'calendar_event_start_date': {
validators: {
notEmpty: {
message: 'La date de début est requise'
}
}
},
'calendar_event_end_date': {
validators: {
notEmpty: {
message: 'La date de fin est requise'
}
}
}
},
plugins: {
trigger: new FormValidation.plugins.Trigger(),
bootstrap: new FormValidation.plugins.Bootstrap5({
rowSelector: '.fv-row',
eleInvalidClass: '',
eleValidClass: ''
})
},
}
);
}
// Handle add button
const handleAddButton = () => {
addButton.addEventListener('click', e => {
// Reset form data
data = {
id: '',
startDate: new Date(),
endDate: new Date(),
allDay: false
};
handleNewEvent();
});
}
const resetFormValidator = (element) => {
// Target modal hidden event --- For more info: https://getbootstrap.com/docs/5.0/components/modal/#events
element.addEventListener('hidden.bs.modal', e => {
if (validator) {
// Reset form validator. For more info: https://formvalidation.io/guide/api/reset-form
validator.resetForm(true);
}
});
}
// Populate form
const populateForm = () => {
eventName.value = data.title ? data.title : '';
startFlatpickr.setDate(data.startDate, true, 'Y-m-d');
// Handle null end dates
const endDate = data.endDate ? data.endDate : moment(data.startDate).format();
endFlatpickr.setDate(endDate, true, 'Y-m-d');
const allDayToggle = form.querySelector('#kt_calendar_datepicker_allday');
const datepickerWrappers = form.querySelectorAll('[data-kt-calendar="datepicker"]');
if (data.allDay) {
allDayToggle.checked = true;
datepickerWrappers.forEach(dw => {
dw.classList.add('d-none');
});
} else {
startTimeFlatpickr.setDate(data.startDate, true, 'Y-m-d H:i');
endTimeFlatpickr.setDate(data.endDate, true, 'Y-m-d H:i');
endFlatpickr.setDate(data.endDate, true, 'Y-m-d');
allDayToggle.checked = false;
datepickerWrappers.forEach(dw => {
dw.classList.remove('d-none');
});
}
}
const formatArgs = (res) => {
console.log(res);
data.id = res.id;
data.title = res.title;
data.startDate = res.startStr;
data.endDate = res.endStr;
data.allDay = res.allDay;
}
const uid = () => {
return Date.now().toString() + Math.floor(Math.random() * 1000).toString();
}
return {
init: function () {
const element = document.getElementById('kt_modal_add_event');
if (element != undefined) {
form = element.querySelector('#kt_modal_add_event_form');
eventName = form.querySelector('[name="calendar_event_name"]');
eventDescription = form.querySelector('[name="calendar_event_description"]');
eventLocation = form.querySelector('[name="calendar_event_location"]');
startDatepicker = form.querySelector('#kt_calendar_datepicker_start_date');
endDatepicker = form.querySelector('#kt_calendar_datepicker_end_date');
startTimepicker = form.querySelector('#kt_calendar_datepicker_start_time');
endTimepicker = form.querySelector('#kt_calendar_datepicker_end_time');
addButton = document.querySelector('[data-kt-calendar="add"]');
submitButton = form.querySelector('#kt_modal_add_event_submit');
cancelButton = form.querySelector('#kt_modal_add_event_cancel');
closeButton = element.querySelector('#kt_modal_add_event_close');
modalTitle = form.querySelector('[data-kt-calendar="title"]');
modal = new bootstrap.Modal(element);
const viewElement = document.getElementById('kt_modal_view_event');
viewModal = new bootstrap.Modal(viewElement);
viewEventName = viewElement.querySelector('[data-kt-calendar="event_name"]');
viewAllDay = viewElement.querySelector('[data-kt-calendar="all_day"]');
viewEventDescription = viewElement.querySelector('[data-kt-calendar="event_description"]');
viewEventLocation = viewElement.querySelector('[data-kt-calendar="event_location"]');
viewStartDate = viewElement.querySelector('[data-kt-calendar="event_start_date"]');
viewEndDate = viewElement.querySelector('[data-kt-calendar="event_end_date"]');
viewEditButton = viewElement.querySelector('#kt_modal_view_event_edit');
viewDeleteButton = viewElement.querySelector('#kt_modal_view_event_delete');
initCalendarApp();
initValidator();
initDatepickers();
handleAddButton();
resetFormValidator(element);
}
}
}
}();
示例:
你知道这可能来自哪里吗?感谢您的帮助。
根据文档,fullcalendar 将结束日期视为排他性。您看到的结束日期是在活动结束后首次定义的。
因此如果选择22日全天,则22日结束后的第一个时刻就是23日午夜。