Для реализации вашего запроса с использованием PHP, jQuery, и Bootstrap, мы создадим простой веб-интерфейс, который будет отображать данные из файла db.json. Вот пошаговое руководство:
db.jsonУбедитесь, что ваш файл db.json находится в корневой директории вашего проекта.
Создайте файл index.php в корневой директории вашего проекта. Этот файл будет обрабатывать запросы и отображать данные.
<?php
// Загрузка данных из файла db.json
$jsonData = file_get_contents('db.json');
$data = json_decode($jsonData, true);
// Получение выбранного folderId из GET-запроса
$folderId = isset($_GET['folderId']) ? $_GET['folderId'] : null;
// Если folderId не указан, показываем список папок
if (!$folderId) {
echo "<h2>Folders</h2>";
echo "<ul>";
foreach ($data['folders'] as $folder) {
echo "<li><a href='?folderId={$folder['id']}'>{$folder['name']}</a></li>";
}
echo "</ul>";
} else {
// Если folderId указан, показываем сниппеты для этой папки
echo "<h2>Snippets</h2>";
echo "<ul>";
foreach ($data['snippets'] as $snippet) {
if ($snippet['folderId'] == $folderId) {
echo "<li><a href='?folderId={$folderId}&snippetId={$snippet['id']}'>{$snippet['name']}</a></li>";
}
}
echo "</ul>";
// Если выбран конкретный сниппет, показываем его содержимое
$snippetId = isset($_GET['snippetId']) ? $_GET['snippetId'] : null;
if ($snippetId) {
foreach ($data['snippets'] as $snippet) {
if ($snippet['id'] == $snippetId) {
echo "<h3>{$snippet['name']}</h3>";
echo "<pre>" . htmlspecialchars($snippet['content'][0]['value']) . "</pre>";
break;
}
}
}
}
?>Добавьте ссылки на Bootstrap и jQuery в ваш index.php для улучшения внешнего вида и интерактивности:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>DB JSON Viewer</title> <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet"> <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script> </head> <body> <div class="container"> <?php include 'index.php'; ?> </div> </body> </html>
Разместите ваш проект на веб-сервере (например, Apache или Nginx) и откройте index.php в браузере. Вы увидите список папок, при выборе одной из них — список сниппетов, а при выборе сниппета — его содержимое.
Этот пример демонстрирует базовую функциональность. Вы можете расширить его, добавив больше стилей, улучшив обработку ошибок и добавив другие функции, такие как поиск или фильтрация данных.
Чтобы добавить вкладки (tabs) Bootstrap, если в массиве $snippet['content'] больше одного элемента, можно использовать компонент Tabs из Bootstrap. Мы будем динамически создавать вкладки для каждого элемента в массиве $snippet['content'].
Вот обновленный код для index.php:
<?php
// Загрузка данных из файла db.json
$jsonData = file_get_contents('db.json');
$data = json_decode($jsonData, true);
// Получение выбранного folderId из GET-запроса
$folderId = isset($_GET['folderId']) ? $_GET['folderId'] : null;
// Если folderId не указан, показываем список папок
if (!$folderId) {
echo "<h2>Folders</h2>";
echo "<ul>";
foreach ($data['folders'] as $folder) {
echo "<li><a href='?folderId={$folder['id']}'>{$folder['name']}</a></li>";
}
echo "</ul>";
} else {
// Если folderId указан, показываем сниппеты для этой папки
echo "<h2>Snippets</h2>";
echo "<ul>";
foreach ($data['snippets'] as $snippet) {
if ($snippet['folderId'] == $folderId) {
echo "<li><a href='?folderId={$folderId}&snippetId={$snippet['id']}'>{$snippet['name']}</a></li>";
}
}
echo "</ul>";
// Если выбран конкретный сниппет, показываем его содержимое
$snippetId = isset($_GET['snippetId']) ? $_GET['snippetId'] : null;
if ($snippetId) {
foreach ($data['snippets'] as $snippet) {
if ($snippet['id'] == $snippetId) {
echo "<h3>{$snippet['name']}</h3>";
// Проверяем, сколько элементов в массиве content
if (count($snippet['content']) > 1) {
// Если больше одного элемента, создаем вкладки
echo "<ul class='nav nav-tabs' id='myTab' role='tablist'>";
foreach ($snippet['content'] as $index => $content) {
$active = $index === 0 ? 'active' : '';
echo "<li class='nav-item'>
<a class='nav-link $active' id='tab-$index' data-toggle='tab' href='#content-$index' role='tab' aria-controls='content-$index' aria-selected='true'>
{$content['label']}
</a>
</li>";
}
echo "</ul>";
// Контент для вкладок
echo "<div class='tab-content' id='myTabContent'>";
foreach ($snippet['content'] as $index => $content) {
$active = $index === 0 ? 'show active' : '';
echo "<div class='tab-pane fade $active' id='content-$index' role='tabpanel' aria-labelledby='tab-$index'>
<pre>" . htmlspecialchars($content['value']) . "</pre>
</div>";
}
echo "</div>";
} else {
// Если только один элемент, просто выводим его
echo "<pre>" . htmlspecialchars($snippet['content'][0]['value']) . "</pre>";
}
break;
}
}
}
}
?>Проверка количества элементов в $snippet['content']:
Если элементов больше одного, создаются вкладки (tabs) с использованием Bootstrap.
Если элемент один, выводится его содержимое без вкладок.
Создание вкладок:
Используется компонент Tabs из Bootstrap.
Для каждой вкладки создается ссылка (<a>) с атрибутами data-toggle="tab" и href, указывающим на соответствующий контейнер с контентом.
Контент для вкладок:
Каждый элемент массива $snippet['content'] помещается в отдельный контейнер с классом tab-pane.
Первая вкладка и ее контент помечаются как активные (active и show active).
Подключение Bootstrap и jQuery:
Убедитесь, что в вашем HTML-шаблоне подключены Bootstrap и jQuery, как показано в предыдущем ответе.
Если в $snippet['content'] есть два элемента, то на странице появятся две вкладки:
Первая вкладка будет активной по умолчанию.
При нажатии на вторую вкладку отобразится ее содержимое.
Убедитесь, что в вашем HTML-шаблоне подключены Bootstrap и jQuery:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>DB JSON Viewer</title> <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet"> <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script> </head> <body> <div class="container"> <?php include 'index.php'; ?> </div> </body> </html>
Если в $snippet['content'] один элемент, он будет отображен как есть.
Если элементов несколько, они будут организованы в виде вкладок, что улучшит удобство навигации.
Этот подход делает интерфейс более интерактивным и удобным для пользователя.
The server is busy. Please try again later.
Чтобы добавить редактор кода (например, CodeMirror или Ace Editor) к тегу <pre>, можно использовать библиотеку CodeMirror, так как она проста в настройке и хорошо интегрируется с Bootstrap. Вот как это сделать:
Добавьте ссылки на библиотеку CodeMirror в ваш HTML-файл. CodeMirror требует как CSS, так и JS.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>DB JSON Viewer</title> <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet"> <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script> <!-- Подключение CodeMirror --> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.0/codemirror.min.css"> <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.0/codemirror.min.js"></script> <!-- Поддержка синтаксиса (например, JavaScript) --> <script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.0/mode/javascript/javascript.min.js"></script> </head> <body> <div class="container"> <?php include 'index.php'; ?> </div> <script> // Инициализация CodeMirror для всех элементов <pre> document.querySelectorAll('pre').forEach(pre => { const textarea = document.createElement('textarea'); pre.parentNode.insertBefore(textarea, pre); pre.style.display = 'none'; // Скрываем оригинальный <pre> const editor = CodeMirror.fromTextArea(textarea, { lineNumbers: true, // Номера строк mode: 'javascript', // Режим синтаксиса (можно изменить на другой) readOnly: true, // Редактор только для чтения value: pre.textContent // Вставляем содержимое <pre> в редактор }); }); </script> </body> </html>
В PHP-коде убедитесь, что содержимое сниппета передается в тег <pre>:
<?php
// Загрузка данных из файла db.json
$jsonData = file_get_contents('db.json');
$data = json_decode($jsonData, true);
// Получение выбранного folderId из GET-запроса
$folderId = isset($_GET['folderId']) ? $_GET['folderId'] : null;
// Если folderId не указан, показываем список папок
if (!$folderId) {
echo "<h2>Folders</h2>";
echo "<ul>";
foreach ($data['folders'] as $folder) {
echo "<li><a href='?folderId={$folder['id']}'>{$folder['name']}</a></li>";
}
echo "</ul>";
} else {
// Если folderId указан, показываем сниппеты для этой папки
echo "<h2>Snippets</h2>";
echo "<ul>";
foreach ($data['snippets'] as $snippet) {
if ($snippet['folderId'] == $folderId) {
echo "<li><a href='?folderId={$folderId}&snippetId={$snippet['id']}'>{$snippet['name']}</a></li>";
}
}
echo "</ul>";
// Если выбран конкретный сниппет, показываем его содержимое
$snippetId = isset($_GET['snippetId']) ? $_GET['snippetId'] : null;
if ($snippetId) {
foreach ($data['snippets'] as $snippet) {
if ($snippet['id'] == $snippetId) {
echo "<h3>{$snippet['name']}</h3>";
// Проверяем, сколько элементов в массиве content
if (count($snippet['content']) > 1) {
// Если больше одного элемента, создаем вкладки
echo "<ul class='nav nav-tabs' id='myTab' role='tablist'>";
foreach ($snippet['content'] as $index => $content) {
$active = $index === 0 ? 'active' : '';
echo "<li class='nav-item'>
<a class='nav-link $active' id='tab-$index' data-toggle='tab' href='#content-$index' role='tab' aria-controls='content-$index' aria-selected='true'>
{$content['label']}
</a>
</li>";
}
echo "</ul>";
// Контент для вкладок
echo "<div class='tab-content' id='myTabContent'>";
foreach ($snippet['content'] as $index => $content) {
$active = $index === 0 ? 'show active' : '';
echo "<div class='tab-pane fade $active' id='content-$index' role='tabpanel' aria-labelledby='tab-$index'>
<pre>" . htmlspecialchars($content['value']) . "</pre>
</div>";
}
echo "</div>";
} else {
// Если только один элемент, просто выводим его
echo "<pre>" . htmlspecialchars($snippet['content'][0]['value']) . "</pre>";
}
break;
}
}
}
}
?>CodeMirror заменяет тег <pre> на текстовый редактор.
Содержимое <pre> передается в редактор.
Редактор поддерживает подсветку синтаксиса, номера строк и другие функции.
Редактор настроен как read-only (только для чтения), чтобы пользователи не могли изменять код.
Вы можете настроить CodeMirror под свои нужды, добавив дополнительные параметры:
const editor = CodeMirror.fromTextArea(textarea, { lineNumbers: true, // Номера строк mode: 'javascript', // Режим синтаксиса (например, 'html', 'css', 'python') theme: 'default', // Тема (например, 'dracula', 'material') readOnly: true, // Редактор только для чтения matchBrackets: true, // Подсветка парных скобок autoCloseBrackets: true, // Автозакрытие скобок value: pre.textContent // Вставляем содержимое <pre> в редактор });
Если в $snippet['content'] один элемент, он будет отображен в редакторе кода.
Если элементов несколько, они будут организованы в виде вкладок, и каждый будет отображаться в своем редакторе.
Если вы хотите использовать Ace Editor, замените CodeMirror на Ace:
<!-- Подключение Ace Editor --> <script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.4.12/ace.js"></script> <script> document.querySelectorAll('pre').forEach(pre => { const editorDiv = document.createElement('div'); editorDiv.style.height = '300px'; // Высота редактора pre.parentNode.insertBefore(editorDiv, pre); pre.style.display = 'none'; // Скрываем оригинальный <pre> const editor = ace.edit(editorDiv); editor.setTheme('ace/theme/chrome'); // Тема editor.session.setMode('ace/mode/javascript'); // Режим синтаксиса editor.setReadOnly(true); // Редактор только для чтения editor.setValue(pre.textContent); // Вставляем содержимое <pre> в редактор }); </script>
Теперь ваш интерфейс будет отображать код в удобном редакторе с подсветкой синтаксиса!
Чтобы подключить CKEditor к тегу <pre> и использовать его для отображения и редактирования кода, нужно немного изменить подход, так как CKEditor изначально предназначен для редактирования HTML-контента, а не для работы с кодом. Однако CKEditor поддерживает плагины для работы с кодом, такие как Code Snippet.
Вот как это можно сделать:
Добавьте ссылки на CKEditor в ваш HTML-файл:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>DB JSON Viewer</title> <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet"> <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script> <!-- Подключение CKEditor --> <script src="https://cdn.ckeditor.com/ckeditor5/41.1.0/classic/ckeditor.js"></script> <!-- Плагин для работы с кодом --> <script src="https://cdn.ckeditor.com/ckeditor5/41.1.0/classic/translations/en.js"></script> </head> <body> <div class="container"> <?php include 'index.php'; ?> </div> <script> // Инициализация CKEditor для всех элементов <pre> document.querySelectorAll('pre').forEach(pre => { const editorDiv = document.createElement('div'); editorDiv.classList.add('editor'); // Добавляем класс для стилизации pre.parentNode.insertBefore(editorDiv, pre); pre.style.display = 'none'; // Скрываем оригинальный <pre> // Инициализация CKEditor ClassicEditor .create(editorDiv, { toolbar: { items: [ 'code', // Кнопка для вставки кода '|', 'undo', 'redo', '|', 'heading', '|', 'bold', 'italic', 'link', '|', 'bulletedList', 'numberedList', '|', 'blockQuote', '|', 'codeBlock' // Плагин для блоков кода ] }, language: 'en', // Язык интерфейса codeBlock: { languages: [ { language: 'javascript', label: 'JavaScript' }, { language: 'php', label: 'PHP' }, { language: 'html', label: 'HTML' }, { language: 'css', label: 'CSS' } ] } }) .then(editor => { // Вставляем содержимое <pre> в редактор editor.setData(`<pre><code>${pre.textContent}</code></pre>`); }) .catch(error => { console.error(error); }); }); </script> </body> </html>
В PHP-коде убедитесь, что содержимое сниппета передается в тег <pre>:
<?php
// Загрузка данных из файла db.json
$jsonData = file_get_contents('db.json');
$data = json_decode($jsonData, true);
// Получение выбранного folderId из GET-запроса
$folderId = isset($_GET['folderId']) ? $_GET['folderId'] : null;
// Если folderId не указан, показываем список папок
if (!$folderId) {
echo "<h2>Folders</h2>";
echo "<ul>";
foreach ($data['folders'] as $folder) {
echo "<li><a href='?folderId={$folder['id']}'>{$folder['name']}</a></li>";
}
echo "</ul>";
} else {
// Если folderId указан, показываем сниппеты для этой папки
echo "<h2>Snippets</h2>";
echo "<ul>";
foreach ($data['snippets'] as $snippet) {
if ($snippet['folderId'] == $folderId) {
echo "<li><a href='?folderId={$folderId}&snippetId={$snippet['id']}'>{$snippet['name']}</a></li>";
}
}
echo "</ul>";
// Если выбран конкретный сниппет, показываем его содержимое
$snippetId = isset($_GET['snippetId']) ? $_GET['snippetId'] : null;
if ($snippetId) {
foreach ($data['snippets'] as $snippet) {
if ($snippet['id'] == $snippetId) {
echo "<h3>{$snippet['name']}</h3>";
// Проверяем, сколько элементов в массиве content
if (count($snippet['content']) > 1) {
// Если больше одного элемента, создаем вкладки
echo "<ul class='nav nav-tabs' id='myTab' role='tablist'>";
foreach ($snippet['content'] as $index => $content) {
$active = $index === 0 ? 'active' : '';
echo "<li class='nav-item'>
<a class='nav-link $active' id='tab-$index' data-toggle='tab' href='#content-$index' role='tab' aria-controls='content-$index' aria-selected='true'>
{$content['label']}
</a>
</li>";
}
echo "</ul>";
// Контент для вкладок
echo "<div class='tab-content' id='myTabContent'>";
foreach ($snippet['content'] as $index => $content) {
$active = $index === 0 ? 'show active' : '';
echo "<div class='tab-pane fade $active' id='content-$index' role='tabpanel' aria-labelledby='tab-$index'>
<pre>" . htmlspecialchars($content['value']) . "</pre>
</div>";
}
echo "</div>";
} else {
// Если только один элемент, просто выводим его
echo "<pre>" . htmlspecialchars($snippet['content'][0]['value']) . "</pre>";
}
break;
}
}
}
}
?>CKEditor заменяет тег <pre> на редактор.
Содержимое <pre> передается в редактор.
Используется плагин Code Block для отображения кода с подсветкой синтаксиса.
Редактор поддерживает вставку блоков кода с выбором языка (JavaScript, PHP, HTML, CSS и т.д.).
Вы можете настроить CKEditor, добавив дополнительные плагины или изменив панель инструментов:
ClassicEditor
.create(editorDiv, {
toolbar: {
items: [
'code', // Кнопка для вставки кода
'|',
'undo', 'redo',
'|',
'heading',
'|',
'bold', 'italic', 'link',
'|',
'bulletedList', 'numberedList',
'|',
'blockQuote',
'|',
'codeBlock' // Плагин для блоков кода
]
},
language: 'en', // Язык интерфейса
codeBlock: {
languages: [
{ language: 'javascript', label: 'JavaScript' },
{ language: 'php', label: 'PHP' },
{ language: 'html', label: 'HTML' },
{ language: 'css', label: 'CSS' }
]
}
})
.then(editor => {
// Вставляем содержимое <pre> в редактор
editor.setData(`<pre><code>${pre.textContent}</code></pre>`);
})
.catch(error => {
console.error(error);
});Если в $snippet['content'] один элемент, он будет отображен в CKEditor с подсветкой синтаксиса.
Если элементов несколько, они будут организованы в виде вкладок, и каждый будет отображаться в своем редакторе.
CKEditor больше подходит для редактирования текста и HTML, чем для работы с кодом. Если вам нужен специализированный редактор кода, лучше использовать CodeMirror или Ace Editor.
Если вы хотите, чтобы CKEditor поддерживал больше языков программирования, добавьте их в раздел codeBlock.languages.
Теперь ваш интерфейс будет отображать код в CKEditor с поддержкой подсветки синтаксиса!
Для реализации вашего запроса с использованием FullCalendar, Bootstrap, jQuery и PHP, мы создадим календарь, который будет открывать всплывающее окно (popup) с формой при клике на дату. Затем форма будет отправляться через AJAX и обрабатываться на стороне сервера.
Добавьте ссылки на необходимые библиотеки в ваш HTML-файл:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>FullCalendar Example</title> <!-- Подключение Bootstrap CSS --> <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet"> <!-- Подключение FullCalendar CSS --> <link href="https://cdn.jsdelivr.net/npm/fullcalendar@5.11.3/main.min.css" rel="stylesheet"> </head> <body> <div class="container mt-5"> <div id="calendar"></div> </div> <!-- Модальное окно для формы --> <div class="modal fade" id="eventModal" tabindex="-1" aria-labelledby="eventModalLabel" aria-hidden="true"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="eventModalLabel">Добавить событие</h5> <button type="button" class="close" data-dismiss="modal" aria-label="Close"> <span aria-hidden="true">×</span> </button> </div> <div class="modal-body"> <form id="formbri"> <div class="form-group"> <label for="eventTitle">Заголовок</label> <input type="text" class="form-control" id="eventTitle" name="title" required> </div> <div class="form-group"> <label for="eventDescription">Описание</label> <textarea class="form-control" id="eventDescription" name="description" rows="3"></textarea> </div> <div class="form-group"> <label for="eventDate">Дата</label> <input type="date" class="form-control" id="eventDate" name="date" required> </div> <button type="submit" class="btn btn-primary">Сохранить</button> </form> </div> </div> </div> </div> <!-- Подключение jQuery --> <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script> <!-- Подключение Bootstrap JS --> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script> <!-- Подключение FullCalendar JS --> <script src="https://cdn.jsdelivr.net/npm/fullcalendar@5.11.3/main.min.js"></script> <script> document.addEventListener('DOMContentLoaded', function () { const calendarEl = document.getElementById('calendar'); const calendar = new FullCalendar.Calendar(calendarEl, { initialView: 'dayGridMonth', dateClick: function (info) { // Открываем модальное окно при клике на дату $('#eventDate').val(info.dateStr); // Устанавливаем выбранную дату в поле $('#eventModal').modal('show'); // Показываем модальное окно } }); calendar.render(); }); // Обработка отправки формы через AJAX $('#formbri').on('submit', function (e) { e.preventDefault(); // Предотвращаем стандартную отправку формы const formData = $(this).serialize(); // Сериализуем данные формы $.ajax({ url: 'save_event.php', // URL для отправки данных type: 'POST', data: formData, success: function (response) { alert('Событие успешно сохранено!'); $('#eventModal').modal('hide'); // Закрываем модальное окно calendar.refetchEvents(); // Обновляем события в календаре }, error: function () { alert('Ошибка при сохранении события.'); } }); }); </script> </body> </html>
Создайте файл save_event.php, который будет обрабатывать данные формы и сохранять их (например, в базу данных или файл).
<?php
// save_event.php
// Получаем данные из POST-запроса
$title = $_POST['title'] ?? '';
$description = $_POST['description'] ?? '';
$date = $_POST['date'] ?? '';
// Проверяем, что все поля заполнены
if (empty($title) || empty($date)) {
http_response_code(400); // Ошибка: неверный запрос
echo json_encode(['error' => 'Заполните все обязательные поля.']);
exit;
}
// Пример сохранения данных в файл (можно заменить на запись в базу данных)
$event = [
'title' => $title,
'description' => $description,
'date' => $date
];
// Сохраняем событие в файл (например, events.json)
$events = [];
if (file_exists('events.json')) {
$events = json_decode(file_get_contents('events.json'), true);
}
$events[] = $event;
file_put_contents('events.json', json_encode($events));
// Возвращаем успешный ответ
echo json_encode(['success' => true]);Чтобы отображать сохраненные события в календаре, добавьте загрузку событий через AJAX.
Обновите JavaScript-код:
document.addEventListener('DOMContentLoaded', function () { const calendarEl = document.getElementById('calendar'); const calendar = new FullCalendar.Calendar(calendarEl, { initialView: 'dayGridMonth', dateClick: function (info) { $('#eventDate').val(info.dateStr); // Устанавливаем выбранную дату в поле $('#eventModal').modal('show'); // Показываем модальное окно }, events: function (fetchInfo, successCallback, failureCallback) { // Загружаем события через AJAX $.ajax({ url: 'get_events.php', // URL для получения событий type: 'GET', success: function (response) { successCallback(response); // Передаем события в календарь }, error: function () { failureCallback('Ошибка при загрузке событий.'); } }); } }); calendar.render(); });
Создайте файл get_events.php, который будет возвращать сохраненные события в формате JSON.
<?php
// get_events.php
// Загружаем события из файла (или базы данных)
$events = [];
if (file_exists('events.json')) {
$events = json_decode(file_get_contents('events.json'), true);
}
// Преобразуем события в формат FullCalendar
$formattedEvents = [];
foreach ($events as $event) {
$formattedEvents[] = [
'title' => $event['title'],
'description' => $event['description'],
'start' => $event['date']
];
}
// Возвращаем события в формате JSON
header('Content-Type: application/json');
echo json_encode($formattedEvents);Пользователь кликает на дату в календаре.
Открывается модальное окно с формой.
Пользователь заполняет форму и нажимает "Сохранить".
Данные отправляются через AJAX на сервер.
Сервер сохраняет данные и возвращает ответ.
Календарь обновляется, и событие отображается на выбранной дате.
Добавьте валидацию формы на стороне клиента и сервера.
Реализуйте удаление и редактирование событий.
Используйте базу данных (например, MySQL) для хранения событий.
Теперь у вас есть полнофункциональный календарь с возможностью добавления событий через всплывающее окно!
Ошибка TypeError: calendar.refetchEvents is not a function возникает, потому что переменная calendar не доступна в контексте обработчика события отправки формы. Это связано с тем, что calendar объявлена внутри функции DOMContentLoaded, и она не видна за пределами этой функции.
Чтобы исправить эту ошибку, нужно сделать переменную calendar глобальной или передать её в нужный контекст. Вот исправленный код:
document.addEventListener('DOMContentLoaded', function () { const calendarEl = document.getElementById('calendar'); // Делаем переменную calendar глобальной window.calendar = new FullCalendar.Calendar(calendarEl, { initialView: 'dayGridMonth', dateClick: function (info) { // Открываем модальное окно при клике на дату $('#eventDate').val(info.dateStr); // Устанавливаем выбранную дату в поле $('#eventModal').modal('show'); // Показываем модальное окно }, events: function (fetchInfo, successCallback, failureCallback) { // Загружаем события через AJAX $.ajax({ url: 'get_events.php', // URL для получения событий type: 'GET', success: function (response) { successCallback(response); // Передаем события в календарь }, error: function () { failureCallback('Ошибка при загрузке событий.'); } }); } }); calendar.render(); // Рендерим календарь }); // Обработка отправки формы через AJAX $('#formbri').on('submit', function (e) { e.preventDefault(); // Предотвращаем стандартную отправку формы const formData = $(this).serialize(); // Сериализуем данные формы $.ajax({ url: 'save_event.php', // URL для отправки данных type: 'POST', data: formData, success: function (response) { alert('Событие успешно сохранено!'); $('#eventModal').modal('hide'); // Закрываем модальное окно // Обновляем события в календаре if (window.calendar) { window.calendar.refetchEvents(); // Используем глобальную переменную calendar } }, error: function () { alert('Ошибка при сохранении события.'); } }); });
Глобальная переменная calendar:
Мы объявили переменную calendar как глобальную, добавив её в объект window (window.calendar).
Теперь она доступна в любом месте кода, включая обработчик отправки формы.
Использование window.calendar.refetchEvents():
В обработчике отправки формы мы проверяем, существует ли window.calendar, и вызываем метод refetchEvents() для обновления событий в календаре.
Если вы не хотите использовать глобальные переменные, можно передать calendar в обработчик события через замыкание:
document.addEventListener('DOMContentLoaded', function () { const calendarEl = document.getElementById('calendar'); const calendar = new FullCalendar.Calendar(calendarEl, { initialView: 'dayGridMonth', dateClick: function (info) { $('#eventDate').val(info.dateStr); $('#eventModal').modal('show'); }, events: function (fetchInfo, successCallback, failureCallback) { $.ajax({ url: 'get_events.php', type: 'GET', success: function (response) { successCallback(response); }, error: function () { failureCallback('Ошибка при загрузке событий.'); } }); } }); calendar.render(); // Обработка отправки формы через замыкание $('#formbri').on('submit', function (e) { e.preventDefault(); const formData = $(this).serialize(); $.ajax({ url: 'save_event.php', type: 'POST', data: formData, success: function (response) { alert('Событие успешно сохранено!'); $('#eventModal').modal('hide'); calendar.refetchEvents(); // Используем calendar из замыкания }, error: function () { alert('Ошибка при сохранении события.'); } }); }); });
В этом случае переменная calendar доступна внутри обработчика события отправки формы благодаря замыканию.
Это более чистое решение, чем использование глобальных переменных.
Ошибка calendar.refetchEvents is not a function исправлена.
Теперь календарь обновляет события после успешного сохранения новой записи.
Вы можете выбрать один из двух подходов: использование глобальной переменной или замыкания. Оба работают корректно.
Чтобы добавить возможность редактирования событий в календаре, нужно выполнить следующие шаги:
Добавить обработчик клика на событие в календаре.
Открывать модальное окно с формой для редактирования события.
Заполнять форму данными выбранного события.
Отправлять обновленные данные на сервер через AJAX.
Обновлять календарь после успешного редактирования.
Добавим скрытое поле для хранения ID события (если используется база данных) и кнопку для удаления события.
<!-- Модальное окно для формы --> <div class="modal fade" id="eventModal" tabindex="-1" aria-labelledby="eventModalLabel" aria-hidden="true"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <h5 class="modal-title" id="eventModalLabel">Добавить/Редактировать событие</h5> <button type="button" class="close" data-dismiss="modal" aria-label="Close"> <span aria-hidden="true">×</span> </button> </div> <div class="modal-body"> <form id="formbri"> <input type="hidden" id="eventId" name="id"> <!-- Скрытое поле для ID события --> <div class="form-group"> <label for="eventTitle">Заголовок</label> <input type="text" class="form-control" id="eventTitle" name="title" required> </div> <div class="form-group"> <label for="eventDescription">Описание</label> <textarea class="form-control" id="eventDescription" name="description" rows="3"></textarea> </div> <div class="form-group"> <label for="eventDate">Дата</label> <input type="date" class="form-control" id="eventDate" name="date" required> </div> <button type="submit" class="btn btn-primary">Сохранить</button> <button type="button" class="btn btn-danger" id="deleteEvent">Удалить</button> </form> </div> </div> </div> </div>
Добавим обработчик клика на событие в календаре и логику для редактирования и удаления событий.
document.addEventListener('DOMContentLoaded', function () { const calendarEl = document.getElementById('calendar'); // Инициализация календаря const calendar = new FullCalendar.Calendar(calendarEl, { initialView: 'dayGridMonth', dateClick: function (info) { // Открываем модальное окно для добавления нового события $('#eventId').val(''); // Очищаем ID события $('#eventTitle').val(''); // Очищаем заголовок $('#eventDescription').val(''); // Очищаем описание $('#eventDate').val(info.dateStr); // Устанавливаем выбранную дату $('#eventModal').modal('show'); }, eventClick: function (info) { // Открываем модальное окно для редактирования события const event = info.event; $('#eventId').val(event.id); // Устанавливаем ID события $('#eventTitle').val(event.title); // Устанавливаем заголовок $('#eventDescription').val(event.extendedProps.description); // Устанавливаем описание $('#eventDate').val(event.startStr.substring(0, 10)); // Устанавливаем дату $('#eventModal').modal('show'); }, events: function (fetchInfo, successCallback, failureCallback) { // Загружаем события через AJAX $.ajax({ url: 'get_events.php', type: 'GET', success: function (response) { successCallback(response); }, error: function () { failureCallback('Ошибка при загрузке событий.'); } }); } }); calendar.render(); // Обработка отправки формы через AJAX $('#formbri').on('submit', function (e) { e.preventDefault(); const formData = $(this).serialize(); $.ajax({ url: 'save_event.php', type: 'POST', data: formData, success: function (response) { alert('Событие успешно сохранено!'); $('#eventModal').modal('hide'); calendar.refetchEvents(); // Обновляем события в календаре }, error: function () { alert('Ошибка при сохранении события.'); } }); }); // Обработка удаления события $('#deleteEvent').on('click', function () { const eventId = $('#eventId').val(); if (eventId) { if (confirm('Вы уверены, что хотите удалить это событие?')) { $.ajax({ url: 'delete_event.php', type: 'POST', data: { id: eventId }, success: function (response) { alert('Событие успешно удалено!'); $('#eventModal').modal('hide'); calendar.refetchEvents(); // Обновляем события в календаре }, error: function () { alert('Ошибка при удалении события.'); } }); } } else { alert('Событие не выбрано.'); } }); });
save_event.phpОбновим файл для обработки как создания, так и редактирования событий.
<?php
// save_event.php
// Получаем данные из POST-запроса
$id = $_POST['id'] ?? null;
$title = $_POST['title'] ?? '';
$description = $_POST['description'] ?? '';
$date = $_POST['date'] ?? '';
// Проверяем, что все обязательные поля заполнены
if (empty($title) || empty($date)) {
http_response_code(400); // Ошибка: неверный запрос
echo json_encode(['error' => 'Заполните все обязательные поля.']);
exit;
}
// Загружаем существующие события
$events = [];
if (file_exists('events.json')) {
$events = json_decode(file_get_contents('events.json'), true);
}
// Если ID передан, обновляем существующее событие
if ($id) {
foreach ($events as &$event) {
if ($event['id'] == $id) {
$event['title'] = $title;
$event['description'] = $description;
$event['date'] = $date;
break;
}
}
} else {
// Иначе добавляем новое событие
$events[] = [
'id' => uniqid(), // Генерируем уникальный ID
'title' => $title,
'description' => $description,
'date' => $date
];
}
// Сохраняем события в файл
file_put_contents('events.json', json_encode($events));
// Возвращаем успешный ответ
echo json_encode(['success' => true]);delete_event.phpСоздадим файл для удаления событий.
<?php
// delete_event.php
// Получаем ID события из POST-запроса
$id = $_POST['id'] ?? null;
if (!$id) {
http_response_code(400); // Ошибка: неверный запрос
echo json_encode(['error' => 'ID события не указан.']);
exit;
}
// Загружаем существующие события
$events = [];
if (file_exists('events.json')) {
$events = json_decode(file_get_contents('events.json'), true);
}
// Удаляем событие с указанным ID
$events = array_filter($events, function ($event) use ($id) {
return $event['id'] != $id;
});
// Сохраняем обновленный список событий
file_put_contents('events.json', json_encode(array_values($events)));
// Возвращаем успешный ответ
echo json_encode(['success' => true]);get_events.phpОбновим файл для возврата событий в формате, поддерживающем ID.
<?php
// get_events.php
// Загружаем события из файла
$events = [];
if (file_exists('events.json')) {
$events = json_decode(file_get_contents('events.json'), true);
}
// Преобразуем события в формат FullCalendar
$formattedEvents = [];
foreach ($events as $event) {
$formattedEvents[] = [
'id' => $event['id'], // Добавляем ID события
'title' => $event['title'],
'description' => $event['description'],
'start' => $event['date']
];
}
// Возвращаем события в формате JSON
header('Content-Type: application/json');
echo json_encode($formattedEvents);Редактирование событий:
При клике на событие в календаре открывается модальное окно с заполненной формой.
Пользователь может изменить данные и сохранить их.
Удаление событий:
В модальном окне добавлена кнопка "Удалить", которая удаляет выбранное событие.
Обновление календаря:
После сохранения или удаления события календарь автоматически обновляется.
Теперь ваш календарь поддерживает создание, редактирование и удаление событий!
Time flies when we're having a great chat! We’ve exceeded the length limit. Please start a new chat!